From d79617c279340f29a3a98dc719c9bfa174542a9d Mon Sep 17 00:00:00 2001 From: Emre Tuna Date: Sat, 12 Jul 2025 18:49:23 +0300 Subject: [PATCH] Add fallback support for thumbwheel via REL_HWHEEL --- src/logid/CMakeLists.txt | 240 ++--- src/logid/Configuration.cpp | 152 +-- src/logid/Configuration.h | 116 +-- src/logid/Device.cpp | 732 +++++++------- src/logid/Device.h | 374 +++---- src/logid/DeviceManager.cpp | 640 ++++++------ src/logid/DeviceManager.h | 232 ++--- src/logid/InputDevice.cpp | 366 +++---- src/logid/InputDevice.h | 178 ++-- src/logid/Receiver.cpp | 468 ++++----- src/logid/Receiver.h | 198 ++-- src/logid/actions/Action.cpp | 286 +++--- src/logid/actions/Action.h | 198 ++-- src/logid/actions/ChangeDPI.cpp | 188 ++-- src/logid/actions/ChangeDPI.h | 98 +- src/logid/actions/ChangeHostAction.cpp | 224 ++--- src/logid/actions/ChangeHostAction.h | 96 +- src/logid/actions/ChangeProfile.cpp | 134 +-- src/logid/actions/ChangeProfile.h | 94 +- src/logid/actions/CycleDPI.cpp | 196 ++-- src/logid/actions/CycleDPI.h | 98 +- src/logid/actions/GestureAction.cpp | 522 +++++----- src/logid/actions/GestureAction.h | 132 +-- src/logid/actions/KeypressAction.cpp | 252 ++--- src/logid/actions/KeypressAction.h | 102 +- src/logid/actions/NullAction.cpp | 80 +- src/logid/actions/NullAction.h | 88 +- src/logid/actions/ToggleHiresScroll.cpp | 120 +-- src/logid/actions/ToggleHiresScroll.h | 94 +- src/logid/actions/ToggleSmartShift.cpp | 124 +-- src/logid/actions/ToggleSmartShift.h | 94 +- src/logid/actions/gesture/AxisGesture.cpp | 370 +++---- src/logid/actions/gesture/AxisGesture.h | 124 +-- src/logid/actions/gesture/Gesture.cpp | 176 ++-- src/logid/actions/gesture/Gesture.h | 146 +-- src/logid/actions/gesture/IntervalGesture.cpp | 234 ++--- src/logid/actions/gesture/IntervalGesture.h | 116 +-- src/logid/actions/gesture/NullGesture.cpp | 100 +- src/logid/actions/gesture/NullGesture.h | 96 +- src/logid/actions/gesture/ReleaseGesture.cpp | 178 ++-- src/logid/actions/gesture/ReleaseGesture.h | 108 +- .../actions/gesture/ThresholdGesture.cpp | 190 ++-- src/logid/actions/gesture/ThresholdGesture.h | 114 +-- src/logid/backend/Error.cpp | 58 +- src/logid/backend/Error.h | 74 +- src/logid/backend/EventHandlerList.h | 274 +++--- src/logid/backend/hidpp/Device.cpp | 640 ++++++------ src/logid/backend/hidpp/Device.h | 370 +++---- src/logid/backend/hidpp/Report.cpp | 594 +++++------ src/logid/backend/hidpp/Report.h | 268 ++--- src/logid/backend/hidpp/defs.h | 100 +- src/logid/backend/hidpp10/Device.cpp | 328 +++---- src/logid/backend/hidpp10/Device.h | 186 ++-- src/logid/backend/hidpp10/Error.cpp | 136 +-- src/logid/backend/hidpp10/Error.h | 120 +-- src/logid/backend/hidpp10/Receiver.cpp | 760 +++++++------- src/logid/backend/hidpp10/Receiver.h | 440 ++++----- src/logid/backend/hidpp10/ReceiverMonitor.cpp | 504 +++++----- src/logid/backend/hidpp10/ReceiverMonitor.h | 236 ++--- src/logid/backend/hidpp10/defs.h | 64 +- src/logid/backend/hidpp20/Device.cpp | 324 +++--- src/logid/backend/hidpp20/Device.h | 162 +-- src/logid/backend/hidpp20/Error.cpp | 126 +-- src/logid/backend/hidpp20/Error.h | 116 +-- .../backend/hidpp20/EssentialFeature.cpp | 132 +-- src/logid/backend/hidpp20/EssentialFeature.h | 94 +- src/logid/backend/hidpp20/Feature.cpp | 134 +-- src/logid/backend/hidpp20/Feature.h | 124 +-- src/logid/backend/hidpp20/feature_defs.h | 256 ++--- .../hidpp20/features/AdjustableDPI.cpp | 162 +-- .../backend/hidpp20/features/AdjustableDPI.h | 116 +-- .../backend/hidpp20/features/ChangeHost.cpp | 148 +-- .../backend/hidpp20/features/ChangeHost.h | 118 +-- .../backend/hidpp20/features/DeviceName.cpp | 126 +-- .../backend/hidpp20/features/DeviceName.h | 88 +- .../backend/hidpp20/features/FeatureSet.cpp | 96 +- .../backend/hidpp20/features/FeatureSet.h | 98 +- .../backend/hidpp20/features/HiresScroll.cpp | 134 +-- .../backend/hidpp20/features/HiresScroll.h | 180 ++-- .../hidpp20/features/ReprogControls.cpp | 366 +++---- .../backend/hidpp20/features/ReprogControls.h | 350 +++---- src/logid/backend/hidpp20/features/Reset.cpp | 76 +- src/logid/backend/hidpp20/features/Reset.h | 88 +- src/logid/backend/hidpp20/features/Root.cpp | 160 +-- src/logid/backend/hidpp20/features/Root.h | 106 +- .../backend/hidpp20/features/SmartShift.cpp | 250 ++--- .../backend/hidpp20/features/SmartShift.h | 180 ++-- .../backend/hidpp20/features/ThumbWheel.cpp | 156 +-- .../backend/hidpp20/features/ThumbWheel.h | 180 ++-- .../hidpp20/features/WirelessDeviceStatus.cpp | 68 +- .../hidpp20/features/WirelessDeviceStatus.h | 96 +- src/logid/backend/raw/DeviceMonitor.cpp | 388 ++++---- src/logid/backend/raw/DeviceMonitor.h | 200 ++-- src/logid/backend/raw/EventHandler.h | 74 +- src/logid/backend/raw/IOMonitor.cpp | 332 +++---- src/logid/backend/raw/IOMonitor.h | 150 +-- src/logid/backend/raw/RawDevice.cpp | 496 +++++----- src/logid/backend/raw/RawDevice.h | 248 ++--- src/logid/config/config.cpp | 58 +- src/logid/config/group.h | 406 ++++---- src/logid/config/map.h | 120 +-- src/logid/config/schema.h | 670 ++++++------- src/logid/config/types.h | 924 +++++++++--------- src/logid/features/DPI.cpp | 418 ++++---- src/logid/features/DPI.h | 146 +-- src/logid/features/DeviceFeature.h | 186 ++-- src/logid/features/DeviceStatus.cpp | 134 +-- src/logid/features/DeviceStatus.h | 88 +- src/logid/features/HiresScroll.cpp | 636 ++++++------ src/logid/features/HiresScroll.h | 208 ++-- src/logid/features/RemapButton.cpp | 632 ++++++------ src/logid/features/RemapButton.h | 262 ++--- src/logid/features/SmartShift.cpp | 400 ++++---- src/logid/features/SmartShift.h | 154 +-- src/logid/features/ThumbWheel.cpp | 700 ++++++------- src/logid/features/ThumbWheel.h | 196 ++-- src/logid/ipc_defs.h | 48 +- src/logid/logid.cpp | 366 +++---- src/logid/logid.service.in | 26 +- src/logid/logiops-dbus.conf.in | 30 +- src/logid/util/ExceptionHandler.cpp | 74 +- src/logid/util/ExceptionHandler.h | 54 +- src/logid/util/log.cpp | 150 +-- src/logid/util/log.h | 86 +- src/logid/util/task.cpp | 260 ++--- src/logid/util/task.h | 78 +- thumbwheel_fix.patch | 272 ++++++ 127 files changed, 14386 insertions(+), 14114 deletions(-) create mode 100644 thumbwheel_fix.patch diff --git a/src/logid/CMakeLists.txt b/src/logid/CMakeLists.txt index b2616c8..31bebae 100644 --- a/src/logid/CMakeLists.txt +++ b/src/logid/CMakeLists.txt @@ -1,120 +1,120 @@ -cmake_minimum_required(VERSION 3.12) -project(logid) - -# C++20 is only needed for string literal template parameters -set(CMAKE_CXX_STANDARD 20) -set(CMAKE_CXX_STANDARD_REQUIRED ON) - -set(CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/../CMake") - -find_package(Threads REQUIRED) -find_package(PkgConfig REQUIRED) - -add_executable(logid - logid.cpp - util/log.cpp - config/config.cpp - InputDevice.cpp - DeviceManager.cpp - Device.cpp - Receiver.cpp - Configuration.cpp - features/DPI.cpp - features/SmartShift.cpp - features/HiresScroll.cpp - features/RemapButton.cpp - features/DeviceStatus.cpp - features/ThumbWheel.cpp - actions/Action.cpp - actions/NullAction.cpp - actions/KeypressAction.cpp - actions/ToggleHiresScroll.cpp - actions/ToggleSmartShift.cpp - actions/CycleDPI.cpp - actions/ChangeDPI.cpp - actions/GestureAction.cpp - actions/ChangeHostAction.cpp - actions/ChangeProfile.cpp - actions/gesture/Gesture.cpp - actions/gesture/ReleaseGesture.cpp - actions/gesture/ThresholdGesture.cpp - actions/gesture/IntervalGesture.cpp - actions/gesture/AxisGesture.cpp - actions/gesture/NullGesture.cpp - backend/Error.cpp - backend/raw/DeviceMonitor.cpp - backend/raw/RawDevice.cpp - backend/raw/IOMonitor.cpp - backend/hidpp10/Receiver.cpp - backend/hidpp10/ReceiverMonitor.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 - backend/hidpp20/EssentialFeature.cpp - backend/hidpp20/features/Root.cpp - backend/hidpp20/features/FeatureSet.cpp - backend/hidpp20/features/DeviceName.cpp - backend/hidpp20/features/Reset.cpp - backend/hidpp20/features/AdjustableDPI.cpp - backend/hidpp20/features/SmartShift.cpp - backend/hidpp20/features/ReprogControls.cpp - backend/hidpp20/features/HiresScroll.cpp - backend/hidpp20/features/ChangeHost.cpp - backend/hidpp20/features/WirelessDeviceStatus.cpp - backend/hidpp20/features/ThumbWheel.cpp - util/task.cpp - util/ExceptionHandler.cpp) - -set_target_properties(logid PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}) - -pkg_check_modules(PC_EVDEV libevdev REQUIRED) -pkg_check_modules(SYSTEMD "systemd") -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) - -set(IPCGULL_INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/../ipcgull/src/include) -message(${IPCGULL_INCLUDE_DIRS}) - -include_directories(. ${EVDEV_INCLUDE_DIR} ${LIBUDEV_INCLUDE_DIRECTORIES} ${IPCGULL_INCLUDE_DIRS}) - -target_link_libraries(logid ${CMAKE_THREAD_LIBS_INIT} ${EVDEV_LIBRARY} config++ - ${LIBUDEV_LIBRARIES} ipcgull) - -install(TARGETS logid DESTINATION bin) - -if (SYSTEMD_FOUND) - if ("${SYSTEMD_SERVICES_INSTALL_DIR}" STREQUAL "") - execute_process(COMMAND ${PKG_CONFIG_EXECUTABLE} - --variable=systemdsystemunitdir systemd - OUTPUT_VARIABLE SYSTEMD_SERVICES_INSTALL_DIR) - string(REGEX REPLACE "[ \t\n]+" "" SYSTEMD_SERVICES_INSTALL_DIR - "${SYSTEMD_SERVICES_INSTALL_DIR}") - endif () - - # Install systemd service - configure_file(logid.service.in ${CMAKE_BINARY_DIR}/logid.service) - message(STATUS "systemd units will be installed at ${SYSTEMD_SERVICES_INSTALL_DIR}") - install(FILES ${CMAKE_BINARY_DIR}/logid.service - DESTINATION ${SYSTEMD_SERVICES_INSTALL_DIR} - COMPONENT cp) -elseif (NOT SYSTEMD_FOUND AND SYSTEMD_SERVICES_INSTALL_DIR) - message(FATAL_ERROR "systemd is not found w/ pkg-config but SYSTEMD_SERVICES_INSTALL_DIR is defined.") -endif () - -# Install DBus conf -# TODO: Is there a better way of setting the system policy directory? -set(DBUS_SYSTEM_POLICY_INSTALL_DIR "/usr/share/dbus-1/system.d") -configure_file(logiops-dbus.conf.in ${CMAKE_BINARY_DIR}/pizza.pixl.LogiOps.conf) -message(STATUS "dbus system policy will be installed at ${DBUS_SYSTEM_POLICY_INSTALL_DIR}") -install(FILES ${CMAKE_BINARY_DIR}/pizza.pixl.LogiOps.conf - DESTINATION ${DBUS_SYSTEM_POLICY_INSTALL_DIR} - COMPONENT cp) +cmake_minimum_required(VERSION 3.12) +project(logid) + +# C++20 is only needed for string literal template parameters +set(CMAKE_CXX_STANDARD 20) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +set(CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/../CMake") + +find_package(Threads REQUIRED) +find_package(PkgConfig REQUIRED) + +add_executable(logid + logid.cpp + util/log.cpp + config/config.cpp + InputDevice.cpp + DeviceManager.cpp + Device.cpp + Receiver.cpp + Configuration.cpp + features/DPI.cpp + features/SmartShift.cpp + features/HiresScroll.cpp + features/RemapButton.cpp + features/DeviceStatus.cpp + features/ThumbWheel.cpp + actions/Action.cpp + actions/NullAction.cpp + actions/KeypressAction.cpp + actions/ToggleHiresScroll.cpp + actions/ToggleSmartShift.cpp + actions/CycleDPI.cpp + actions/ChangeDPI.cpp + actions/GestureAction.cpp + actions/ChangeHostAction.cpp + actions/ChangeProfile.cpp + actions/gesture/Gesture.cpp + actions/gesture/ReleaseGesture.cpp + actions/gesture/ThresholdGesture.cpp + actions/gesture/IntervalGesture.cpp + actions/gesture/AxisGesture.cpp + actions/gesture/NullGesture.cpp + backend/Error.cpp + backend/raw/DeviceMonitor.cpp + backend/raw/RawDevice.cpp + backend/raw/IOMonitor.cpp + backend/hidpp10/Receiver.cpp + backend/hidpp10/ReceiverMonitor.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 + backend/hidpp20/EssentialFeature.cpp + backend/hidpp20/features/Root.cpp + backend/hidpp20/features/FeatureSet.cpp + backend/hidpp20/features/DeviceName.cpp + backend/hidpp20/features/Reset.cpp + backend/hidpp20/features/AdjustableDPI.cpp + backend/hidpp20/features/SmartShift.cpp + backend/hidpp20/features/ReprogControls.cpp + backend/hidpp20/features/HiresScroll.cpp + backend/hidpp20/features/ChangeHost.cpp + backend/hidpp20/features/WirelessDeviceStatus.cpp + backend/hidpp20/features/ThumbWheel.cpp + util/task.cpp + util/ExceptionHandler.cpp) + +set_target_properties(logid PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}) + +pkg_check_modules(PC_EVDEV libevdev REQUIRED) +pkg_check_modules(SYSTEMD "systemd") +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) + +set(IPCGULL_INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/../ipcgull/src/include) +message(${IPCGULL_INCLUDE_DIRS}) + +include_directories(. ${EVDEV_INCLUDE_DIR} ${LIBUDEV_INCLUDE_DIRECTORIES} ${IPCGULL_INCLUDE_DIRS}) + +target_link_libraries(logid ${CMAKE_THREAD_LIBS_INIT} ${EVDEV_LIBRARY} config++ + ${LIBUDEV_LIBRARIES} ipcgull) + +install(TARGETS logid DESTINATION bin) + +if (SYSTEMD_FOUND) + if ("${SYSTEMD_SERVICES_INSTALL_DIR}" STREQUAL "") + execute_process(COMMAND ${PKG_CONFIG_EXECUTABLE} + --variable=systemdsystemunitdir systemd + OUTPUT_VARIABLE SYSTEMD_SERVICES_INSTALL_DIR) + string(REGEX REPLACE "[ \t\n]+" "" SYSTEMD_SERVICES_INSTALL_DIR + "${SYSTEMD_SERVICES_INSTALL_DIR}") + endif () + + # Install systemd service + configure_file(logid.service.in ${CMAKE_BINARY_DIR}/logid.service) + message(STATUS "systemd units will be installed at ${SYSTEMD_SERVICES_INSTALL_DIR}") + install(FILES ${CMAKE_BINARY_DIR}/logid.service + DESTINATION ${SYSTEMD_SERVICES_INSTALL_DIR} + COMPONENT cp) +elseif (NOT SYSTEMD_FOUND AND SYSTEMD_SERVICES_INSTALL_DIR) + message(FATAL_ERROR "systemd is not found w/ pkg-config but SYSTEMD_SERVICES_INSTALL_DIR is defined.") +endif () + +# Install DBus conf +# TODO: Is there a better way of setting the system policy directory? +set(DBUS_SYSTEM_POLICY_INSTALL_DIR "/usr/share/dbus-1/system.d") +configure_file(logiops-dbus.conf.in ${CMAKE_BINARY_DIR}/pizza.pixl.LogiOps.conf) +message(STATUS "dbus system policy will be installed at ${DBUS_SYSTEM_POLICY_INSTALL_DIR}") +install(FILES ${CMAKE_BINARY_DIR}/pizza.pixl.LogiOps.conf + DESTINATION ${DBUS_SYSTEM_POLICY_INSTALL_DIR} + COMPONENT cp) diff --git a/src/logid/Configuration.cpp b/src/logid/Configuration.cpp index e49b5ea..9c437f3 100644 --- a/src/logid/Configuration.cpp +++ b/src/logid/Configuration.cpp @@ -1,76 +1,76 @@ -/* - * Copyright 2019-2023 PixlOne - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * 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 - -using namespace logid; -using namespace libconfig; -using namespace logid::config; - -Configuration::Configuration(std::string config_file) : - _config_file(std::move(config_file)) { - if (std::filesystem::exists(_config_file)) { - 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; - } catch (const ParseException& e) { - logPrintf(ERROR, "Parse error in %s, line %d: %s", e.getFile(), - e.getLine(), e.getError()); - throw; - } - - Config::operator=(get(_config.getRoot())); - } else { - logPrintf(INFO, "Config file does not exist, using empty config."); - } - - if (!devices.has_value()) - devices.emplace(); -} - -Configuration::Configuration() { - devices.emplace(); -} - -void Configuration::save() { - config::set(_config.getRoot(), *this); - try { - _config.writeFile(_config_file.c_str()); - } catch (const FileIOException& e) { - logPrintf(ERROR, "I/O Error while writing %s: %s", - _config_file.c_str(), e.what()); - throw; - } catch (const std::exception& e) { - logPrintf(ERROR, "Error while writing %s: %s", - _config_file.c_str(), e.what()); - throw; - } -} - -Configuration::IPC::IPC(Configuration* config) : - ipcgull::interface(SERVICE_ROOT_NAME ".Config", { - {"Save", {config, &Configuration::save}} - }, {}, {}) { -} +/* + * Copyright 2019-2023 PixlOne + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * 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 + +using namespace logid; +using namespace libconfig; +using namespace logid::config; + +Configuration::Configuration(std::string config_file) : + _config_file(std::move(config_file)) { + if (std::filesystem::exists(_config_file)) { + 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; + } catch (const ParseException& e) { + logPrintf(ERROR, "Parse error in %s, line %d: %s", e.getFile(), + e.getLine(), e.getError()); + throw; + } + + Config::operator=(get(_config.getRoot())); + } else { + logPrintf(INFO, "Config file does not exist, using empty config."); + } + + if (!devices.has_value()) + devices.emplace(); +} + +Configuration::Configuration() { + devices.emplace(); +} + +void Configuration::save() { + config::set(_config.getRoot(), *this); + try { + _config.writeFile(_config_file.c_str()); + } catch (const FileIOException& e) { + logPrintf(ERROR, "I/O Error while writing %s: %s", + _config_file.c_str(), e.what()); + throw; + } catch (const std::exception& e) { + logPrintf(ERROR, "Error while writing %s: %s", + _config_file.c_str(), e.what()); + throw; + } +} + +Configuration::IPC::IPC(Configuration* config) : + ipcgull::interface(SERVICE_ROOT_NAME ".Config", { + {"Save", {config, &Configuration::save}} + }, {}, {}) { +} diff --git a/src/logid/Configuration.h b/src/logid/Configuration.h index 3a6a1e6..262ef82 100644 --- a/src/logid/Configuration.h +++ b/src/logid/Configuration.h @@ -1,58 +1,58 @@ -/* - * Copyright 2019-2023 PixlOne - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * 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 - -#include -#include -#include -#include -#include -#include - -namespace logid { - namespace defaults { - static constexpr double io_timeout = 500; - static constexpr int workers = 4; - static constexpr int gesture_threshold = 50; - } - - class Configuration : public config::Config { - public: - explicit Configuration(std::string config_file); - - Configuration(); - - // Reloading is not safe, references will be invalidated - //void reload(); - void save(); - - class IPC : public ipcgull::interface { - public: - explicit IPC(Configuration* config); - }; - - private: - std::string _config_file; - libconfig::Config _config; - }; - -} - -#endif //LOGID_CONFIGURATION_H +/* + * Copyright 2019-2023 PixlOne + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * 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 + +#include +#include +#include +#include +#include +#include + +namespace logid { + namespace defaults { + static constexpr double io_timeout = 500; + static constexpr int workers = 4; + static constexpr int gesture_threshold = 50; + } + + class Configuration : public config::Config { + public: + explicit Configuration(std::string config_file); + + Configuration(); + + // Reloading is not safe, references will be invalidated + //void reload(); + void save(); + + class IPC : public ipcgull::interface { + public: + explicit IPC(Configuration* config); + }; + + private: + std::string _config_file; + libconfig::Config _config; + }; + +} + +#endif //LOGID_CONFIGURATION_H diff --git a/src/logid/Device.cpp b/src/logid/Device.cpp index 20b679d..0134e28 100644 --- a/src/logid/Device.cpp +++ b/src/logid/Device.cpp @@ -1,366 +1,366 @@ -/* - * Copyright 2019-2023 PixlOne - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * 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 -#include -#include -#include -#include -#include - -using namespace logid; -using namespace logid::backend; - -DeviceNickname::DeviceNickname(const std::shared_ptr& manager) : - _nickname(manager->newDeviceNickname()), _manager(manager) { -} - -DeviceNickname::operator std::string() const { - return std::to_string(_nickname); -} - -DeviceNickname::~DeviceNickname() { - if (auto manager = _manager.lock()) { - std::lock_guard lock(manager->_nick_lock); - manager->_device_nicknames.erase(_nickname); - } -} - -namespace logid { - class DeviceWrapper : public Device { - public: - template - explicit DeviceWrapper(Args... args) : Device(std::forward(args)...) {} - }; -} - -std::shared_ptr Device::make( - std::string path, backend::hidpp::DeviceIndex index, - std::shared_ptr manager) { - auto ret = std::make_shared(std::move(path), - index, - std::move(manager)); - ret->_self = ret; - ret->_ipc_node->manage(ret); - ret->_ipc_interface = ret->_ipc_node->make_interface(ret.get()); - return ret; -} - -std::shared_ptr Device::make( - std::shared_ptr raw_device, - backend::hidpp::DeviceIndex index, - std::shared_ptr manager) { - auto ret = std::make_shared(std::move(raw_device), - index, - std::move(manager)); - ret->_self = ret; - ret->_ipc_node->manage(ret); - ret->_ipc_interface = ret->_ipc_node->make_interface(ret.get()); - return ret; -} - -std::shared_ptr Device::make( - Receiver* receiver, backend::hidpp::DeviceIndex index, - std::shared_ptr manager) { - auto ret = std::make_shared(receiver, index, std::move(manager)); - ret->_self = ret; - ret->_ipc_node->manage(ret); - ret->_ipc_interface = ret->_ipc_node->make_interface(ret.get()); - return ret; -} - -Device::Device(std::string path, backend::hidpp::DeviceIndex index, - const std::shared_ptr& manager) : - _hidpp20(hidpp20::Device::make(path, index, manager, - manager->config()->io_timeout.value_or( - defaults::io_timeout))), - _path(std::move(path)), _index(index), - _config(_getConfig(manager, _hidpp20->name())), - _profile_name(ipcgull::property_readable, ""), - _manager(manager), - _nickname(manager), - _ipc_node(manager->devicesNode()->make_child(_nickname)), - _awake(ipcgull::property_readable, true) { - _init(); -} - -Device::Device(std::shared_ptr raw_device, - hidpp::DeviceIndex index, const std::shared_ptr& manager) : - _hidpp20(hidpp20::Device::make( - std::move(raw_device), index, - manager->config()->io_timeout.value_or(defaults::io_timeout))), - _path(raw_device->rawPath()), _index(index), - _config(_getConfig(manager, _hidpp20->name())), - _profile_name(ipcgull::property_readable, ""), - _manager(manager), - _nickname(manager), - _ipc_node(manager->devicesNode()->make_child(_nickname)), - _awake(ipcgull::property_readable, true) { - _init(); -} - -Device::Device(Receiver* receiver, hidpp::DeviceIndex index, - const std::shared_ptr& manager) : - _hidpp20(hidpp20::Device::make( - receiver->rawReceiver(), index, - manager->config()->io_timeout.value_or(defaults::io_timeout))), - _path(receiver->path()), _index(index), - _config(_getConfig(manager, _hidpp20->name())), - _profile_name(ipcgull::property_readable, ""), - _manager(manager), - _nickname(manager), - _ipc_node(manager->devicesNode()->make_child(_nickname)), - _awake(ipcgull::property_readable, true) { - _init(); -} - -void Device::_init() { - logPrintf(INFO, "Device found: %s on %s:%d", name().c_str(), - hidpp20().devicePath().c_str(), _index); - - { - std::unique_lock lock(_profile_mutex); - _profile = _config.profiles.find(_config.default_profile); - if (_profile == _config.profiles.end()) - _profile = _config.profiles.insert({_config.default_profile, {}}).first; - _profile_name = _config.default_profile; - } - - _addFeature("dpi"); - _addFeature("smartshift"); - _addFeature("hiresscroll"); - _addFeature("remapbutton"); - _addFeature("devicestatus"); - _addFeature("thumbwheel"); - - _makeResetMechanism(); - reset(); - - for (auto& feature: _features) { - feature.second->configure(); - feature.second->listen(); - } -} - -std::string Device::name() { - return _hidpp20->name(); -} - -uint16_t Device::pid() { - return _hidpp20->pid(); -} - -void Device::sleep() { - std::lock_guard lock(_state_lock); - if (_awake) { - logPrintf(INFO, "%s:%d fell asleep.", _path.c_str(), _index); - _awake = false; - _ipc_interface->notifyStatus(); - } -} - -void Device::wakeup() { - std::lock_guard lock(_state_lock); - - reconfigure(); - - if (!_awake) { - _awake = true; - _ipc_interface->notifyStatus(); - } - - logPrintf(INFO, "%s:%d woke up.", _path.c_str(), _index); -} - -void Device::reconfigure() { - 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); -} - -std::shared_ptr Device::virtualInput() const { - if (auto manager = _manager.lock()) { - return manager->virtualInput(); - } else { - logPrintf(ERROR, "Device manager lost"); - logPrintf(ERROR, - "Fatal error occurred, file a bug report," - " the program will now exit."); - std::terminate(); - } -} - -std::shared_ptr Device::ipcNode() const { - return _ipc_node; -} - -std::vector Device::getProfiles() const { - std::shared_lock lock(_profile_mutex); - - std::vector ret; - for (auto& profile : _config.profiles) { - ret.push_back(profile.first); - } - - return ret; -} - -void Device::setProfile(const std::string& profile) { - std::unique_lock lock(_profile_mutex); - - _profile = _config.profiles.find(profile); - if (_profile == _config.profiles.end()) - _profile = _config.profiles.insert({profile, {}}).first; - _profile_name = profile; - - for (auto& feature : _features) - feature.second->setProfile(_profile->second); - - reconfigure(); -} - -void Device::setProfileDelayed(const std::string& profile) { - run_task([self_weak = _self, profile](){ - if (auto self = self_weak.lock()) - self->setProfile(profile); - }); -} - -void Device::removeProfile(const std::string& profile) { - std::unique_lock lock(_profile_mutex); - - if (profile == (std::string)_profile_name) - throw std::invalid_argument("cannot remove active profile"); - else if (profile == (std::string)_config.default_profile) - throw std::invalid_argument("cannot remove default profile"); - - _config.profiles.erase(profile); -} - -void Device::clearProfile(const std::string& profile) { - std::unique_lock lock(_profile_mutex); - - if (profile == (std::string)_profile_name) { - _profile->second = config::Profile(); - - for (auto& feature : _features) - feature.second->setProfile(_profile->second); - - reconfigure(); - } else { - auto it = _config.profiles.find(profile); - if (it != _config.profiles.end()) { - it->second = config::Profile(); - } else { - throw std::invalid_argument("unknown profile"); - } - } -} - -config::Profile& Device::activeProfile() { - std::shared_lock lock(_profile_mutex); - return _profile->second; -} - -hidpp20::Device& Device::hidpp20() { - return *_hidpp20; -} - -void Device::_makeResetMechanism() { - try { - hidpp20::Reset reset(_hidpp20.get()); - _reset_mechanism = std::make_unique>( - [dev = _hidpp20] { - hidpp20::Reset reset(dev.get()); - reset.reset(reset.getProfile()); - }); - } catch (hidpp20::UnsupportedFeature& e) { - // Reset unsupported, ignore. - } -} - -Device::IPC::IPC(Device* device) : - ipcgull::interface( - SERVICE_ROOT_NAME ".Device", - { - {"GetProfiles", {device, &Device::getProfiles, {"profiles"}}}, - {"SetProfile", {device, &Device::setProfile, {"profile"}}}, - {"RemoveProfile", {device, &Device::removeProfile, {"profile"}}}, - {"ClearProfile", {device, &Device::clearProfile, {"profile"}}} - }, - { - {"Name", ipcgull::property( - ipcgull::property_readable, device->name())}, - {"ProductID", ipcgull::property( - ipcgull::property_readable, device->pid())}, - {"Active", device->_awake}, - {"DefaultProfile", device->_config.default_profile}, - {"ActiveProfile", device->_profile_name} - }, { - {"StatusChanged", ipcgull::signal::make_signal({"active"})} - }), _device(*device) { -} - -void Device::IPC::notifyStatus() const { - emit_signal("StatusChanged", (bool) (_device._awake)); -} - -config::Device& Device::_getConfig( - const std::shared_ptr& manager, - const std::string& name) { - static std::mutex config_mutex; - std::lock_guard lock(config_mutex); - auto& devices = manager->config()->devices.value(); - - if (!devices.count(name)) { - devices.emplace(name, config::Device()); - } - - auto& device = devices.at(name); - if (std::holds_alternative(device)) { - config::Device d; - d.profiles["default"] = std::get(device); - d.default_profile = "default"; - device = std::move(d); - } - - auto& conf = std::get(device); - if (conf.profiles.empty()) { - conf.profiles["default"] = {}; - conf.default_profile = "default"; - } - - return conf; -} +/* + * Copyright 2019-2023 PixlOne + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * 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 +#include +#include +#include +#include +#include + +using namespace logid; +using namespace logid::backend; + +DeviceNickname::DeviceNickname(const std::shared_ptr& manager) : + _nickname(manager->newDeviceNickname()), _manager(manager) { +} + +DeviceNickname::operator std::string() const { + return std::to_string(_nickname); +} + +DeviceNickname::~DeviceNickname() { + if (auto manager = _manager.lock()) { + std::lock_guard lock(manager->_nick_lock); + manager->_device_nicknames.erase(_nickname); + } +} + +namespace logid { + class DeviceWrapper : public Device { + public: + template + explicit DeviceWrapper(Args... args) : Device(std::forward(args)...) {} + }; +} + +std::shared_ptr Device::make( + std::string path, backend::hidpp::DeviceIndex index, + std::shared_ptr manager) { + auto ret = std::make_shared(std::move(path), + index, + std::move(manager)); + ret->_self = ret; + ret->_ipc_node->manage(ret); + ret->_ipc_interface = ret->_ipc_node->make_interface(ret.get()); + return ret; +} + +std::shared_ptr Device::make( + std::shared_ptr raw_device, + backend::hidpp::DeviceIndex index, + std::shared_ptr manager) { + auto ret = std::make_shared(std::move(raw_device), + index, + std::move(manager)); + ret->_self = ret; + ret->_ipc_node->manage(ret); + ret->_ipc_interface = ret->_ipc_node->make_interface(ret.get()); + return ret; +} + +std::shared_ptr Device::make( + Receiver* receiver, backend::hidpp::DeviceIndex index, + std::shared_ptr manager) { + auto ret = std::make_shared(receiver, index, std::move(manager)); + ret->_self = ret; + ret->_ipc_node->manage(ret); + ret->_ipc_interface = ret->_ipc_node->make_interface(ret.get()); + return ret; +} + +Device::Device(std::string path, backend::hidpp::DeviceIndex index, + const std::shared_ptr& manager) : + _hidpp20(hidpp20::Device::make(path, index, manager, + manager->config()->io_timeout.value_or( + defaults::io_timeout))), + _path(std::move(path)), _index(index), + _config(_getConfig(manager, _hidpp20->name())), + _profile_name(ipcgull::property_readable, ""), + _manager(manager), + _nickname(manager), + _ipc_node(manager->devicesNode()->make_child(_nickname)), + _awake(ipcgull::property_readable, true) { + _init(); +} + +Device::Device(std::shared_ptr raw_device, + hidpp::DeviceIndex index, const std::shared_ptr& manager) : + _hidpp20(hidpp20::Device::make( + std::move(raw_device), index, + manager->config()->io_timeout.value_or(defaults::io_timeout))), + _path(raw_device->rawPath()), _index(index), + _config(_getConfig(manager, _hidpp20->name())), + _profile_name(ipcgull::property_readable, ""), + _manager(manager), + _nickname(manager), + _ipc_node(manager->devicesNode()->make_child(_nickname)), + _awake(ipcgull::property_readable, true) { + _init(); +} + +Device::Device(Receiver* receiver, hidpp::DeviceIndex index, + const std::shared_ptr& manager) : + _hidpp20(hidpp20::Device::make( + receiver->rawReceiver(), index, + manager->config()->io_timeout.value_or(defaults::io_timeout))), + _path(receiver->path()), _index(index), + _config(_getConfig(manager, _hidpp20->name())), + _profile_name(ipcgull::property_readable, ""), + _manager(manager), + _nickname(manager), + _ipc_node(manager->devicesNode()->make_child(_nickname)), + _awake(ipcgull::property_readable, true) { + _init(); +} + +void Device::_init() { + logPrintf(INFO, "Device found: %s on %s:%d", name().c_str(), + hidpp20().devicePath().c_str(), _index); + + { + std::unique_lock lock(_profile_mutex); + _profile = _config.profiles.find(_config.default_profile); + if (_profile == _config.profiles.end()) + _profile = _config.profiles.insert({_config.default_profile, {}}).first; + _profile_name = _config.default_profile; + } + + _addFeature("dpi"); + _addFeature("smartshift"); + _addFeature("hiresscroll"); + _addFeature("remapbutton"); + _addFeature("devicestatus"); + _addFeature("thumbwheel"); + + _makeResetMechanism(); + reset(); + + for (auto& feature: _features) { + feature.second->configure(); + feature.second->listen(); + } +} + +std::string Device::name() { + return _hidpp20->name(); +} + +uint16_t Device::pid() { + return _hidpp20->pid(); +} + +void Device::sleep() { + std::lock_guard lock(_state_lock); + if (_awake) { + logPrintf(INFO, "%s:%d fell asleep.", _path.c_str(), _index); + _awake = false; + _ipc_interface->notifyStatus(); + } +} + +void Device::wakeup() { + std::lock_guard lock(_state_lock); + + reconfigure(); + + if (!_awake) { + _awake = true; + _ipc_interface->notifyStatus(); + } + + logPrintf(INFO, "%s:%d woke up.", _path.c_str(), _index); +} + +void Device::reconfigure() { + 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); +} + +std::shared_ptr Device::virtualInput() const { + if (auto manager = _manager.lock()) { + return manager->virtualInput(); + } else { + logPrintf(ERROR, "Device manager lost"); + logPrintf(ERROR, + "Fatal error occurred, file a bug report," + " the program will now exit."); + std::terminate(); + } +} + +std::shared_ptr Device::ipcNode() const { + return _ipc_node; +} + +std::vector Device::getProfiles() const { + std::shared_lock lock(_profile_mutex); + + std::vector ret; + for (auto& profile : _config.profiles) { + ret.push_back(profile.first); + } + + return ret; +} + +void Device::setProfile(const std::string& profile) { + std::unique_lock lock(_profile_mutex); + + _profile = _config.profiles.find(profile); + if (_profile == _config.profiles.end()) + _profile = _config.profiles.insert({profile, {}}).first; + _profile_name = profile; + + for (auto& feature : _features) + feature.second->setProfile(_profile->second); + + reconfigure(); +} + +void Device::setProfileDelayed(const std::string& profile) { + run_task([self_weak = _self, profile](){ + if (auto self = self_weak.lock()) + self->setProfile(profile); + }); +} + +void Device::removeProfile(const std::string& profile) { + std::unique_lock lock(_profile_mutex); + + if (profile == (std::string)_profile_name) + throw std::invalid_argument("cannot remove active profile"); + else if (profile == (std::string)_config.default_profile) + throw std::invalid_argument("cannot remove default profile"); + + _config.profiles.erase(profile); +} + +void Device::clearProfile(const std::string& profile) { + std::unique_lock lock(_profile_mutex); + + if (profile == (std::string)_profile_name) { + _profile->second = config::Profile(); + + for (auto& feature : _features) + feature.second->setProfile(_profile->second); + + reconfigure(); + } else { + auto it = _config.profiles.find(profile); + if (it != _config.profiles.end()) { + it->second = config::Profile(); + } else { + throw std::invalid_argument("unknown profile"); + } + } +} + +config::Profile& Device::activeProfile() { + std::shared_lock lock(_profile_mutex); + return _profile->second; +} + +hidpp20::Device& Device::hidpp20() { + return *_hidpp20; +} + +void Device::_makeResetMechanism() { + try { + hidpp20::Reset reset(_hidpp20.get()); + _reset_mechanism = std::make_unique>( + [dev = _hidpp20] { + hidpp20::Reset reset(dev.get()); + reset.reset(reset.getProfile()); + }); + } catch (hidpp20::UnsupportedFeature& e) { + // Reset unsupported, ignore. + } +} + +Device::IPC::IPC(Device* device) : + ipcgull::interface( + SERVICE_ROOT_NAME ".Device", + { + {"GetProfiles", {device, &Device::getProfiles, {"profiles"}}}, + {"SetProfile", {device, &Device::setProfile, {"profile"}}}, + {"RemoveProfile", {device, &Device::removeProfile, {"profile"}}}, + {"ClearProfile", {device, &Device::clearProfile, {"profile"}}} + }, + { + {"Name", ipcgull::property( + ipcgull::property_readable, device->name())}, + {"ProductID", ipcgull::property( + ipcgull::property_readable, device->pid())}, + {"Active", device->_awake}, + {"DefaultProfile", device->_config.default_profile}, + {"ActiveProfile", device->_profile_name} + }, { + {"StatusChanged", ipcgull::signal::make_signal({"active"})} + }), _device(*device) { +} + +void Device::IPC::notifyStatus() const { + emit_signal("StatusChanged", (bool) (_device._awake)); +} + +config::Device& Device::_getConfig( + const std::shared_ptr& manager, + const std::string& name) { + static std::mutex config_mutex; + std::lock_guard lock(config_mutex); + auto& devices = manager->config()->devices.value(); + + if (!devices.count(name)) { + devices.emplace(name, config::Device()); + } + + auto& device = devices.at(name); + if (std::holds_alternative(device)) { + config::Device d; + d.profiles["default"] = std::get(device); + d.default_profile = "default"; + device = std::move(d); + } + + auto& conf = std::get(device); + if (conf.profiles.empty()) { + conf.profiles["default"] = {}; + conf.default_profile = "default"; + } + + return conf; +} diff --git a/src/logid/Device.h b/src/logid/Device.h index 99da6dc..a601258 100644 --- a/src/logid/Device.h +++ b/src/logid/Device.h @@ -1,187 +1,187 @@ -/* - * Copyright 2019-2023 PixlOne - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * 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 - -#include -#include -#include -#include -#include -#include - -namespace logid { - class DeviceManager; - - class Device; - - class Receiver; - - class InputDevice; - - class DeviceNickname { - public: - explicit DeviceNickname(const std::shared_ptr& manager); - - DeviceNickname() = delete; - - DeviceNickname(const DeviceNickname&) = delete; - - ~DeviceNickname(); - - operator std::string() const; - - private: - const int _nickname; - const std::weak_ptr _manager; - }; - - /* TODO: Implement HID++ 1.0 support - * Currently, the logid::Device class has a hardcoded requirement - * for an HID++ 2.0 device. - */ - class Device : public ipcgull::object { - public: - std::string name(); - - uint16_t pid(); - - [[nodiscard]] config::Profile& activeProfile(); - - [[nodiscard]] std::vector getProfiles() const; - - void setProfile(const std::string& profile); - - void setProfileDelayed(const std::string& profile); - - void removeProfile(const std::string& profile); - - void clearProfile(const std::string& profile); - - backend::hidpp20::Device& hidpp20(); - - static std::shared_ptr make( - std::string path, - backend::hidpp::DeviceIndex index, - std::shared_ptr manager); - - static std::shared_ptr make( - std::shared_ptr raw_device, - backend::hidpp::DeviceIndex index, - std::shared_ptr manager); - - static std::shared_ptr make( - Receiver* receiver, - backend::hidpp::DeviceIndex index, - std::shared_ptr manager); - - void wakeup(); - - void sleep(); - - void reconfigure(); - - void reset(); - - [[nodiscard]] std::shared_ptr virtualInput() const; - - [[nodiscard]] std::shared_ptr ipcNode() const; - - template - std::shared_ptr getFeature(const 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) { - return nullptr; - } - } - - Device(const Device&) = delete; - - Device(Device&&) = delete; - - private: - friend class DeviceWrapper; - - Device(std::string path, backend::hidpp::DeviceIndex index, - const std::shared_ptr& manager); - - Device(std::shared_ptr raw_device, - backend::hidpp::DeviceIndex index, - const std::shared_ptr& manager); - - Device(Receiver* receiver, backend::hidpp::DeviceIndex index, - const std::shared_ptr& manager); - - static config::Device& _getConfig( - const std::shared_ptr& manager, - const std::string& name); - - void _init(); - - /* Adds a feature without calling an error if unsupported */ - template - void _addFeature(std::string name) { - try { - _features.emplace(name, features::DeviceFeature::make(this)); - } catch (features::UnsupportedFeature& e) { - } - } - - std::shared_ptr _hidpp20; - std::string _path; - backend::hidpp::DeviceIndex _index; - std::map> _features; - - config::Device& _config; - mutable std::shared_mutex _profile_mutex; - ipcgull::property _profile_name; - std::map::iterator _profile; - - const std::weak_ptr _manager; - - void _makeResetMechanism(); - - std::unique_ptr> _reset_mechanism; - - const DeviceNickname _nickname; - std::shared_ptr _ipc_node; - - class IPC : public ipcgull::interface { - private: - Device& _device; - public: - explicit IPC(Device* device); - - void notifyStatus() const; - }; - - ipcgull::property _awake; - std::mutex _state_lock; - - std::weak_ptr _self; - - std::shared_ptr _ipc_interface; - }; -} - -#endif //LOGID_DEVICE_H +/* + * Copyright 2019-2023 PixlOne + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * 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 + +#include +#include +#include +#include +#include +#include + +namespace logid { + class DeviceManager; + + class Device; + + class Receiver; + + class InputDevice; + + class DeviceNickname { + public: + explicit DeviceNickname(const std::shared_ptr& manager); + + DeviceNickname() = delete; + + DeviceNickname(const DeviceNickname&) = delete; + + ~DeviceNickname(); + + operator std::string() const; + + private: + const int _nickname; + const std::weak_ptr _manager; + }; + + /* TODO: Implement HID++ 1.0 support + * Currently, the logid::Device class has a hardcoded requirement + * for an HID++ 2.0 device. + */ + class Device : public ipcgull::object { + public: + std::string name(); + + uint16_t pid(); + + [[nodiscard]] config::Profile& activeProfile(); + + [[nodiscard]] std::vector getProfiles() const; + + void setProfile(const std::string& profile); + + void setProfileDelayed(const std::string& profile); + + void removeProfile(const std::string& profile); + + void clearProfile(const std::string& profile); + + backend::hidpp20::Device& hidpp20(); + + static std::shared_ptr make( + std::string path, + backend::hidpp::DeviceIndex index, + std::shared_ptr manager); + + static std::shared_ptr make( + std::shared_ptr raw_device, + backend::hidpp::DeviceIndex index, + std::shared_ptr manager); + + static std::shared_ptr make( + Receiver* receiver, + backend::hidpp::DeviceIndex index, + std::shared_ptr manager); + + void wakeup(); + + void sleep(); + + void reconfigure(); + + void reset(); + + [[nodiscard]] std::shared_ptr virtualInput() const; + + [[nodiscard]] std::shared_ptr ipcNode() const; + + template + std::shared_ptr getFeature(const 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) { + return nullptr; + } + } + + Device(const Device&) = delete; + + Device(Device&&) = delete; + + private: + friend class DeviceWrapper; + + Device(std::string path, backend::hidpp::DeviceIndex index, + const std::shared_ptr& manager); + + Device(std::shared_ptr raw_device, + backend::hidpp::DeviceIndex index, + const std::shared_ptr& manager); + + Device(Receiver* receiver, backend::hidpp::DeviceIndex index, + const std::shared_ptr& manager); + + static config::Device& _getConfig( + const std::shared_ptr& manager, + const std::string& name); + + void _init(); + + /* Adds a feature without calling an error if unsupported */ + template + void _addFeature(std::string name) { + try { + _features.emplace(name, features::DeviceFeature::make(this)); + } catch (features::UnsupportedFeature& e) { + } + } + + std::shared_ptr _hidpp20; + std::string _path; + backend::hidpp::DeviceIndex _index; + std::map> _features; + + config::Device& _config; + mutable std::shared_mutex _profile_mutex; + ipcgull::property _profile_name; + std::map::iterator _profile; + + const std::weak_ptr _manager; + + void _makeResetMechanism(); + + std::unique_ptr> _reset_mechanism; + + const DeviceNickname _nickname; + std::shared_ptr _ipc_node; + + class IPC : public ipcgull::interface { + private: + Device& _device; + public: + explicit IPC(Device* device); + + void notifyStatus() const; + }; + + ipcgull::property _awake; + std::mutex _state_lock; + + std::weak_ptr _self; + + std::shared_ptr _ipc_interface; + }; +} + +#endif //LOGID_DEVICE_H diff --git a/src/logid/DeviceManager.cpp b/src/logid/DeviceManager.cpp index e41ac30..ee2959e 100644 --- a/src/logid/DeviceManager.cpp +++ b/src/logid/DeviceManager.cpp @@ -1,320 +1,320 @@ -/* - * Copyright 2019-2023 PixlOne - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * 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 - -using namespace logid; -using namespace logid::backend; - -DeviceManager::DeviceManager(std::shared_ptr config, - std::shared_ptr virtual_input, - std::shared_ptr server) : - backend::raw::DeviceMonitor(), - _server(std::move(server)), _config(std::move(config)), - _virtual_input(std::move(virtual_input)), - _root_node(ipcgull::node::make_root("")), - _device_node(ipcgull::node::make_root("devices")), - _receiver_node(ipcgull::node::make_root("receivers")) { - _ipc_devices = _root_node->make_interface(this); - _ipc_receivers = _root_node->make_interface(this); - _ipc_config = _root_node->make_interface(_config.get()); - _device_node->add_server(_server); - _receiver_node->add_server(_server); - _root_node->add_server(_server); -} - -std::shared_ptr DeviceManager::config() const { - return _config; -} - -std::shared_ptr DeviceManager::virtualInput() const { - return _virtual_input; -} - -std::shared_ptr DeviceManager::devicesNode() const { - return _device_node; -} - -std::shared_ptr DeviceManager::receiversNode() const { - return _receiver_node; -} - -void DeviceManager::addDevice(std::string path) { - bool defaultExists = true; - bool isReceiver = false; - - // Check if device is ignored before continuing - { - auto raw_dev = raw::RawDevice::make(path, self().lock()); - if (config()->ignore.has_value() && - config()->ignore.value().contains(raw_dev->productId())) { - logPrintf(DEBUG, "%s: Device 0x%04x ignored.", - path.c_str(), raw_dev->productId()); - return; - } - } - - try { - auto device = hidpp::Device::make( - path, hidpp::DefaultDevice, self().lock(), - config()->io_timeout.value_or(defaults::io_timeout)); - isReceiver = device->version() == std::make_tuple(1, 0); - } catch (hidpp20::Error& e) { - if (e.code() != hidpp20::Error::UnknownDevice) - throw DeviceNotReady(); - defaultExists = false; - } catch (hidpp10::Error& e) { - if (e.code() != hidpp10::Error::UnknownDevice) - throw DeviceNotReady(); - defaultExists = false; - } catch (hidpp::Device::InvalidDevice& e) { - if (e.code() == hidpp::Device::InvalidDevice::VirtualNode) { - logPrintf(DEBUG, "Ignoring virtual node on %s", path.c_str()); - } else if (e.code() == hidpp::Device::InvalidDevice::Asleep) { - /* May be a valid device, wait */ - throw DeviceNotReady(); - } - - return; - } catch (std::system_error& e) { - logPrintf(WARN, "I/O error on %s: %s, skipping device.", path.c_str(), e.what()); - return; - } catch (TimeoutError& e) { - /* Ready and valid non-default devices should throw an UnknownDevice error */ - throw DeviceNotReady(); - } - - if (isReceiver) { - logPrintf(INFO, "Detected receiver at %s", path.c_str()); - auto receiver = Receiver::make(path, self().lock()); - std::lock_guard lock(_map_lock); - _receivers.emplace(path, receiver); - _ipc_receivers->receiverAdded(receiver); - } else { - /* 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 = Device::make(path, hidpp::DefaultDevice, self().lock()); - std::lock_guard lock(_map_lock); - _devices.emplace(path, device); - _ipc_devices->deviceAdded(device); - } else { - try { - auto device = Device::make(path, hidpp::CordedDevice, self().lock()); - std::lock_guard lock(_map_lock); - _devices.emplace(path, device); - _ipc_devices->deviceAdded(device); - } catch (hidpp10::Error& e) { - if (e.code() != hidpp10::Error::UnknownDevice) - throw DeviceNotReady(); - } catch (hidpp20::Error& e) { - if (e.code() != hidpp20::Error::UnknownDevice) - throw DeviceNotReady(); - } catch (hidpp::Device::InvalidDevice& e) { - if (e.code() == hidpp::Device::InvalidDevice::Asleep) - throw DeviceNotReady(); - } catch (std::system_error& e) { - // This error should have been thrown previously - logPrintf(WARN, "I/O error on %s: %s", path.c_str(), e.what()); - } catch (TimeoutError& e) { - throw DeviceNotReady(); - } - } - } -} - -void DeviceManager::addExternalDevice(const std::shared_ptr& d) { - _ipc_devices->deviceAdded(d); -} - -void DeviceManager::removeExternalDevice(const std::shared_ptr& d) { - _ipc_devices->deviceRemoved(d); -} - -std::mutex& DeviceManager::mutex() const { - return _map_lock; -} - -void DeviceManager::removeDevice(std::string path) { - std::lock_guard lock(_map_lock); - auto receiver = _receivers.find(path); - - if (receiver != _receivers.end()) { - _ipc_receivers->receiverRemoved(receiver->second); - _receivers.erase(receiver); - logPrintf(INFO, "Receiver on %s disconnected", path.c_str()); - } else { - auto device = _devices.find(path); - if (device != _devices.end()) { - _ipc_devices->deviceRemoved(device->second); - _devices.erase(device); - logPrintf(INFO, "Device on %s disconnected", path.c_str()); - } - } -} - -DeviceManager::DevicesIPC::DevicesIPC(DeviceManager* manager) : - ipcgull::interface( - SERVICE_ROOT_NAME ".Devices", - { - {"Enumerate", {manager, &DeviceManager::listDevices, {"devices"}}} - }, - {}, - { - {"DeviceAdded", - ipcgull::make_signal>( - {"device"})}, - {"DeviceRemoved", - ipcgull::make_signal>( - {"device"})} - }) { -} - -std::vector> DeviceManager::listDevices() const { - std::lock_guard lock(_map_lock); - std::vector> devices; - for (auto& x: _devices) - devices.emplace_back(x.second); - for (auto& x: _receivers) { - for (auto& d: x.second->devices()) - devices.emplace_back(d.second); - } - - return devices; -} - -std::vector> DeviceManager::listReceivers() const { - std::lock_guard lock(_map_lock); - std::vector> receivers; - for (auto& x: _receivers) - receivers.emplace_back(x.second); - return receivers; -} - -void DeviceManager::DevicesIPC::deviceAdded( - const std::shared_ptr& d) { - emit_signal("DeviceAdded", d); -} - -void DeviceManager::DevicesIPC::deviceRemoved( - const std::shared_ptr& d) { - emit_signal("DeviceRemoved", d); -} - -DeviceManager::ReceiversIPC::ReceiversIPC(DeviceManager* manager) : - ipcgull::interface( - SERVICE_ROOT_NAME ".Receivers", - { - {"Enumerate", {manager, &DeviceManager::listReceivers, - {"receivers"}}} - }, - {}, - { - {"ReceiverAdded", - ipcgull::make_signal>( - {"receiver"})}, - {"ReceiverRemoved", - ipcgull::make_signal>( - {"receiver"})} - }) { -} - -void DeviceManager::ReceiversIPC::receiverAdded( - const std::shared_ptr& r) { - emit_signal("ReceiverAdded", r); -} - -void DeviceManager::ReceiversIPC::receiverRemoved( - const std::shared_ptr& r) { - emit_signal("ReceiverRemoved", r); -} - -int DeviceManager::newDeviceNickname() { - std::lock_guard lock(_nick_lock); - - auto begin = _device_nicknames.begin(); - if (begin != _device_nicknames.end()) { - if (*begin != 0) { - _device_nicknames.insert(0); - return 0; - } - } - - const auto i = std::adjacent_find(_device_nicknames.begin(), - _device_nicknames.end(), - [](int l, int r) { return l + 1 < r; }); - - - if (i == _device_nicknames.end()) { - auto end = _device_nicknames.rbegin(); - if (end != _device_nicknames.rend()) { - auto ret = *end + 1; - assert(ret > 0); - _device_nicknames.insert(ret); - return ret; - } else { - _device_nicknames.insert(0); - return 0; - } - } - - auto ret = *i + 1; - assert(ret > 0); - _device_nicknames.insert(ret); - return ret; -} - -int DeviceManager::newReceiverNickname() { - std::lock_guard lock(_nick_lock); - - auto begin = _receiver_nicknames.begin(); - if (begin != _receiver_nicknames.end()) { - if (*begin != 0) { - _receiver_nicknames.insert(0); - return 0; - } - } - - const auto i = std::adjacent_find(_receiver_nicknames.begin(), - _receiver_nicknames.end(), - [](int l, int r) { return l + 1 < r; }); - - if (i == _receiver_nicknames.end()) { - auto end = _receiver_nicknames.rbegin(); - if (end != _receiver_nicknames.rend()) { - auto ret = *end + 1; - assert(ret > 0); - _receiver_nicknames.insert(ret); - return ret; - } else { - _receiver_nicknames.insert(0); - return 0; - } - } - - auto ret = *i + 1; - assert(ret > 0); - _receiver_nicknames.insert(ret); - return ret; -} +/* + * Copyright 2019-2023 PixlOne + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * 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 + +using namespace logid; +using namespace logid::backend; + +DeviceManager::DeviceManager(std::shared_ptr config, + std::shared_ptr virtual_input, + std::shared_ptr server) : + backend::raw::DeviceMonitor(), + _server(std::move(server)), _config(std::move(config)), + _virtual_input(std::move(virtual_input)), + _root_node(ipcgull::node::make_root("")), + _device_node(ipcgull::node::make_root("devices")), + _receiver_node(ipcgull::node::make_root("receivers")) { + _ipc_devices = _root_node->make_interface(this); + _ipc_receivers = _root_node->make_interface(this); + _ipc_config = _root_node->make_interface(_config.get()); + _device_node->add_server(_server); + _receiver_node->add_server(_server); + _root_node->add_server(_server); +} + +std::shared_ptr DeviceManager::config() const { + return _config; +} + +std::shared_ptr DeviceManager::virtualInput() const { + return _virtual_input; +} + +std::shared_ptr DeviceManager::devicesNode() const { + return _device_node; +} + +std::shared_ptr DeviceManager::receiversNode() const { + return _receiver_node; +} + +void DeviceManager::addDevice(std::string path) { + bool defaultExists = true; + bool isReceiver = false; + + // Check if device is ignored before continuing + { + auto raw_dev = raw::RawDevice::make(path, self().lock()); + if (config()->ignore.has_value() && + config()->ignore.value().contains(raw_dev->productId())) { + logPrintf(DEBUG, "%s: Device 0x%04x ignored.", + path.c_str(), raw_dev->productId()); + return; + } + } + + try { + auto device = hidpp::Device::make( + path, hidpp::DefaultDevice, self().lock(), + config()->io_timeout.value_or(defaults::io_timeout)); + isReceiver = device->version() == std::make_tuple(1, 0); + } catch (hidpp20::Error& e) { + if (e.code() != hidpp20::Error::UnknownDevice) + throw DeviceNotReady(); + defaultExists = false; + } catch (hidpp10::Error& e) { + if (e.code() != hidpp10::Error::UnknownDevice) + throw DeviceNotReady(); + defaultExists = false; + } catch (hidpp::Device::InvalidDevice& e) { + if (e.code() == hidpp::Device::InvalidDevice::VirtualNode) { + logPrintf(DEBUG, "Ignoring virtual node on %s", path.c_str()); + } else if (e.code() == hidpp::Device::InvalidDevice::Asleep) { + /* May be a valid device, wait */ + throw DeviceNotReady(); + } + + return; + } catch (std::system_error& e) { + logPrintf(WARN, "I/O error on %s: %s, skipping device.", path.c_str(), e.what()); + return; + } catch (TimeoutError& e) { + /* Ready and valid non-default devices should throw an UnknownDevice error */ + throw DeviceNotReady(); + } + + if (isReceiver) { + logPrintf(INFO, "Detected receiver at %s", path.c_str()); + auto receiver = Receiver::make(path, self().lock()); + std::lock_guard lock(_map_lock); + _receivers.emplace(path, receiver); + _ipc_receivers->receiverAdded(receiver); + } else { + /* 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 = Device::make(path, hidpp::DefaultDevice, self().lock()); + std::lock_guard lock(_map_lock); + _devices.emplace(path, device); + _ipc_devices->deviceAdded(device); + } else { + try { + auto device = Device::make(path, hidpp::CordedDevice, self().lock()); + std::lock_guard lock(_map_lock); + _devices.emplace(path, device); + _ipc_devices->deviceAdded(device); + } catch (hidpp10::Error& e) { + if (e.code() != hidpp10::Error::UnknownDevice) + throw DeviceNotReady(); + } catch (hidpp20::Error& e) { + if (e.code() != hidpp20::Error::UnknownDevice) + throw DeviceNotReady(); + } catch (hidpp::Device::InvalidDevice& e) { + if (e.code() == hidpp::Device::InvalidDevice::Asleep) + throw DeviceNotReady(); + } catch (std::system_error& e) { + // This error should have been thrown previously + logPrintf(WARN, "I/O error on %s: %s", path.c_str(), e.what()); + } catch (TimeoutError& e) { + throw DeviceNotReady(); + } + } + } +} + +void DeviceManager::addExternalDevice(const std::shared_ptr& d) { + _ipc_devices->deviceAdded(d); +} + +void DeviceManager::removeExternalDevice(const std::shared_ptr& d) { + _ipc_devices->deviceRemoved(d); +} + +std::mutex& DeviceManager::mutex() const { + return _map_lock; +} + +void DeviceManager::removeDevice(std::string path) { + std::lock_guard lock(_map_lock); + auto receiver = _receivers.find(path); + + if (receiver != _receivers.end()) { + _ipc_receivers->receiverRemoved(receiver->second); + _receivers.erase(receiver); + logPrintf(INFO, "Receiver on %s disconnected", path.c_str()); + } else { + auto device = _devices.find(path); + if (device != _devices.end()) { + _ipc_devices->deviceRemoved(device->second); + _devices.erase(device); + logPrintf(INFO, "Device on %s disconnected", path.c_str()); + } + } +} + +DeviceManager::DevicesIPC::DevicesIPC(DeviceManager* manager) : + ipcgull::interface( + SERVICE_ROOT_NAME ".Devices", + { + {"Enumerate", {manager, &DeviceManager::listDevices, {"devices"}}} + }, + {}, + { + {"DeviceAdded", + ipcgull::make_signal>( + {"device"})}, + {"DeviceRemoved", + ipcgull::make_signal>( + {"device"})} + }) { +} + +std::vector> DeviceManager::listDevices() const { + std::lock_guard lock(_map_lock); + std::vector> devices; + for (auto& x: _devices) + devices.emplace_back(x.second); + for (auto& x: _receivers) { + for (auto& d: x.second->devices()) + devices.emplace_back(d.second); + } + + return devices; +} + +std::vector> DeviceManager::listReceivers() const { + std::lock_guard lock(_map_lock); + std::vector> receivers; + for (auto& x: _receivers) + receivers.emplace_back(x.second); + return receivers; +} + +void DeviceManager::DevicesIPC::deviceAdded( + const std::shared_ptr& d) { + emit_signal("DeviceAdded", d); +} + +void DeviceManager::DevicesIPC::deviceRemoved( + const std::shared_ptr& d) { + emit_signal("DeviceRemoved", d); +} + +DeviceManager::ReceiversIPC::ReceiversIPC(DeviceManager* manager) : + ipcgull::interface( + SERVICE_ROOT_NAME ".Receivers", + { + {"Enumerate", {manager, &DeviceManager::listReceivers, + {"receivers"}}} + }, + {}, + { + {"ReceiverAdded", + ipcgull::make_signal>( + {"receiver"})}, + {"ReceiverRemoved", + ipcgull::make_signal>( + {"receiver"})} + }) { +} + +void DeviceManager::ReceiversIPC::receiverAdded( + const std::shared_ptr& r) { + emit_signal("ReceiverAdded", r); +} + +void DeviceManager::ReceiversIPC::receiverRemoved( + const std::shared_ptr& r) { + emit_signal("ReceiverRemoved", r); +} + +int DeviceManager::newDeviceNickname() { + std::lock_guard lock(_nick_lock); + + auto begin = _device_nicknames.begin(); + if (begin != _device_nicknames.end()) { + if (*begin != 0) { + _device_nicknames.insert(0); + return 0; + } + } + + const auto i = std::adjacent_find(_device_nicknames.begin(), + _device_nicknames.end(), + [](int l, int r) { return l + 1 < r; }); + + + if (i == _device_nicknames.end()) { + auto end = _device_nicknames.rbegin(); + if (end != _device_nicknames.rend()) { + auto ret = *end + 1; + assert(ret > 0); + _device_nicknames.insert(ret); + return ret; + } else { + _device_nicknames.insert(0); + return 0; + } + } + + auto ret = *i + 1; + assert(ret > 0); + _device_nicknames.insert(ret); + return ret; +} + +int DeviceManager::newReceiverNickname() { + std::lock_guard lock(_nick_lock); + + auto begin = _receiver_nicknames.begin(); + if (begin != _receiver_nicknames.end()) { + if (*begin != 0) { + _receiver_nicknames.insert(0); + return 0; + } + } + + const auto i = std::adjacent_find(_receiver_nicknames.begin(), + _receiver_nicknames.end(), + [](int l, int r) { return l + 1 < r; }); + + if (i == _receiver_nicknames.end()) { + auto end = _receiver_nicknames.rbegin(); + if (end != _receiver_nicknames.rend()) { + auto ret = *end + 1; + assert(ret > 0); + _receiver_nicknames.insert(ret); + return ret; + } else { + _receiver_nicknames.insert(0); + return 0; + } + } + + auto ret = *i + 1; + assert(ret > 0); + _receiver_nicknames.insert(ret); + return ret; +} diff --git a/src/logid/DeviceManager.h b/src/logid/DeviceManager.h index 77105da..812b3ef 100644 --- a/src/logid/DeviceManager.h +++ b/src/logid/DeviceManager.h @@ -1,116 +1,116 @@ -/* - * Copyright 2019-2023 PixlOne - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * 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 - -#include -#include -#include -#include -#include - -namespace logid { - class InputDevice; - - class DeviceManager : public backend::raw::DeviceMonitor { - public: - - [[nodiscard]] std::shared_ptr config() const; - - [[nodiscard]] std::shared_ptr virtualInput() const; - - [[nodiscard]] std::shared_ptr devicesNode() const; - - [[nodiscard]] std::shared_ptr - receiversNode() const; - - void addExternalDevice(const std::shared_ptr& d); - - void removeExternalDevice(const std::shared_ptr& d); - - std::mutex& mutex() const; - - protected: - DeviceManager(std::shared_ptr config, - std::shared_ptr virtual_input, - std::shared_ptr server); - - void addDevice(std::string path) final; - - void removeDevice(std::string path) final; - - private: - class DevicesIPC : public ipcgull::interface { - public: - explicit DevicesIPC(DeviceManager* manager); - - void deviceAdded(const std::shared_ptr& d); - - void deviceRemoved(const std::shared_ptr& d); - }; - - [[nodiscard]] - std::vector> listDevices() const; - - class ReceiversIPC : public ipcgull::interface { - public: - explicit ReceiversIPC(DeviceManager* manager); - - void receiverAdded(const std::shared_ptr& r); - - void receiverRemoved(const std::shared_ptr& r); - }; - - [[nodiscard]] - std::vector> listReceivers() const; - - std::shared_ptr _server; - std::shared_ptr _config; - std::shared_ptr _virtual_input; - - std::shared_ptr _root_node; - - std::shared_ptr _device_node; - std::shared_ptr _receiver_node; - - std::shared_ptr _ipc_config; - std::shared_ptr _ipc_devices; - std::shared_ptr _ipc_receivers; - - std::map> _devices; - std::map> _receivers; - - mutable std::mutex _map_lock; - - friend class DeviceNickname; - - friend class ReceiverNickname; - - [[nodiscard]] int newDeviceNickname(); - - [[nodiscard]] int newReceiverNickname(); - - std::mutex _nick_lock; - std::set _device_nicknames; - std::set _receiver_nicknames; - }; - -} - -#endif //LOGID_DEVICEMANAGER_H +/* + * Copyright 2019-2023 PixlOne + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * 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 + +#include +#include +#include +#include +#include + +namespace logid { + class InputDevice; + + class DeviceManager : public backend::raw::DeviceMonitor { + public: + + [[nodiscard]] std::shared_ptr config() const; + + [[nodiscard]] std::shared_ptr virtualInput() const; + + [[nodiscard]] std::shared_ptr devicesNode() const; + + [[nodiscard]] std::shared_ptr + receiversNode() const; + + void addExternalDevice(const std::shared_ptr& d); + + void removeExternalDevice(const std::shared_ptr& d); + + std::mutex& mutex() const; + + protected: + DeviceManager(std::shared_ptr config, + std::shared_ptr virtual_input, + std::shared_ptr server); + + void addDevice(std::string path) final; + + void removeDevice(std::string path) final; + + private: + class DevicesIPC : public ipcgull::interface { + public: + explicit DevicesIPC(DeviceManager* manager); + + void deviceAdded(const std::shared_ptr& d); + + void deviceRemoved(const std::shared_ptr& d); + }; + + [[nodiscard]] + std::vector> listDevices() const; + + class ReceiversIPC : public ipcgull::interface { + public: + explicit ReceiversIPC(DeviceManager* manager); + + void receiverAdded(const std::shared_ptr& r); + + void receiverRemoved(const std::shared_ptr& r); + }; + + [[nodiscard]] + std::vector> listReceivers() const; + + std::shared_ptr _server; + std::shared_ptr _config; + std::shared_ptr _virtual_input; + + std::shared_ptr _root_node; + + std::shared_ptr _device_node; + std::shared_ptr _receiver_node; + + std::shared_ptr _ipc_config; + std::shared_ptr _ipc_devices; + std::shared_ptr _ipc_receivers; + + std::map> _devices; + std::map> _receivers; + + mutable std::mutex _map_lock; + + friend class DeviceNickname; + + friend class ReceiverNickname; + + [[nodiscard]] int newDeviceNickname(); + + [[nodiscard]] int newReceiverNickname(); + + std::mutex _nick_lock; + std::set _device_nicknames; + std::set _receiver_nicknames; + }; + +} + +#endif //LOGID_DEVICEMANAGER_H diff --git a/src/logid/InputDevice.cpp b/src/logid/InputDevice.cpp index 9d3169c..956c320 100644 --- a/src/logid/InputDevice.cpp +++ b/src/logid/InputDevice.cpp @@ -1,183 +1,183 @@ -/* - * Copyright 2019-2023 PixlOne - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * 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 - -extern "C" -{ -#include -#include -} - -using namespace logid; - -InputDevice::InvalidEventCode::InvalidEventCode(const std::string& name) : - _what("Invalid event code " + name) { -} - -InputDevice::InvalidEventCode::InvalidEventCode(uint code) : - _what("Invalid event code " + std::to_string(code)) { -} - -const char* InputDevice::InvalidEventCode::what() const noexcept { - return _what.c_str(); -} - -InputDevice::InputDevice(const char* name) { - device = libevdev_new(); - libevdev_set_name(device, name); - - libevdev_enable_event_type(device, EV_KEY); - for (unsigned int i = 0; i < KEY_CNT; i++) { - // Enable some keys which a normal keyboard should have - // by default, i.e. a-z, modifier keys and so on, see: - // /usr/include/linux/input-event-codes.h - if (i < 128) { - registered_keys[i] = true; - libevdev_enable_event_code(device, EV_KEY, i, nullptr); - } else { - registered_keys[i] = false; - } - } - - for (bool& axis: registered_axis) - axis = false; - - libevdev_enable_event_type(device, EV_REL); - - int err = libevdev_uinput_create_from_device(device, - LIBEVDEV_UINPUT_OPEN_MANAGED, &ui_device); - - if (err != 0) { - libevdev_free(device); - throw std::system_error(-err, std::generic_category()); - } -} - -InputDevice::~InputDevice() { - libevdev_uinput_destroy(ui_device); - libevdev_free(device); -} - -void InputDevice::registerKey(uint code) { - // TODO: Maybe print error message, if wrong code is passed? - if (code >= KEY_CNT || registered_keys[code]) { - return; - } - - _enableEvent(EV_KEY, code); - - registered_keys[code] = true; -} - -void InputDevice::registerAxis(uint axis) { - // TODO: Maybe print error message, if wrong code is passed? - if (axis >= REL_CNT || registered_axis[axis]) { - return; - } - - _enableEvent(EV_REL, axis); - - registered_axis[axis] = true; -} - -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); -} - -std::string InputDevice::toKeyName(uint code) { - return _toEventName(EV_KEY, code); -} - -uint InputDevice::toKeyCode(const std::string& name) { - return _toEventCode(EV_KEY, name); -} - -std::string InputDevice::toAxisName(uint code) { - return _toEventName(EV_REL, code); -} - -uint InputDevice::toAxisCode(const std::string& name) { - return _toEventCode(EV_REL, name); -} - -/* Returns -1 if axis_code is not hi-res */ -int InputDevice::getLowResAxis(const uint axis_code) { - /* Some systems don't have these hi-res axes */ -#ifdef REL_WHEEL_HI_RES - if (axis_code == REL_WHEEL_HI_RES) - return REL_WHEEL; -#endif -#ifdef REL_HWHEEL_HI_RES - if (axis_code == REL_HWHEEL_HI_RES) - return REL_HWHEEL; -#endif - - return -1; -} - -std::string InputDevice::_toEventName(uint type, uint code) { - const char* ret = libevdev_event_code_get_name(type, code); - - if (!ret) - throw InvalidEventCode(code); - - return {ret}; -} - -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::_enableEvent(const uint type, const uint code) { - std::unique_lock lock(_input_mutex); - libevdev_uinput_destroy(ui_device); - - libevdev_enable_event_code(device, type, code, nullptr); - - int err = libevdev_uinput_create_from_device(device, - LIBEVDEV_UINPUT_OPEN_MANAGED, &ui_device); - - if (err != 0) { - libevdev_free(device); - device = nullptr; - ui_device = nullptr; - throw std::system_error(-err, std::generic_category()); - } -} - -void InputDevice::_sendEvent(uint type, uint code, int value) { - std::unique_lock lock(_input_mutex); - libevdev_uinput_write_event(ui_device, type, code, value); - libevdev_uinput_write_event(ui_device, EV_SYN, SYN_REPORT, 0); -} +/* + * Copyright 2019-2023 PixlOne + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * 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 + +extern "C" +{ +#include +#include +} + +using namespace logid; + +InputDevice::InvalidEventCode::InvalidEventCode(const std::string& name) : + _what("Invalid event code " + name) { +} + +InputDevice::InvalidEventCode::InvalidEventCode(uint code) : + _what("Invalid event code " + std::to_string(code)) { +} + +const char* InputDevice::InvalidEventCode::what() const noexcept { + return _what.c_str(); +} + +InputDevice::InputDevice(const char* name) { + device = libevdev_new(); + libevdev_set_name(device, name); + + libevdev_enable_event_type(device, EV_KEY); + for (unsigned int i = 0; i < KEY_CNT; i++) { + // Enable some keys which a normal keyboard should have + // by default, i.e. a-z, modifier keys and so on, see: + // /usr/include/linux/input-event-codes.h + if (i < 128) { + registered_keys[i] = true; + libevdev_enable_event_code(device, EV_KEY, i, nullptr); + } else { + registered_keys[i] = false; + } + } + + for (bool& axis: registered_axis) + axis = false; + + libevdev_enable_event_type(device, EV_REL); + + int err = libevdev_uinput_create_from_device(device, + LIBEVDEV_UINPUT_OPEN_MANAGED, &ui_device); + + if (err != 0) { + libevdev_free(device); + throw std::system_error(-err, std::generic_category()); + } +} + +InputDevice::~InputDevice() { + libevdev_uinput_destroy(ui_device); + libevdev_free(device); +} + +void InputDevice::registerKey(uint code) { + // TODO: Maybe print error message, if wrong code is passed? + if (code >= KEY_CNT || registered_keys[code]) { + return; + } + + _enableEvent(EV_KEY, code); + + registered_keys[code] = true; +} + +void InputDevice::registerAxis(uint axis) { + // TODO: Maybe print error message, if wrong code is passed? + if (axis >= REL_CNT || registered_axis[axis]) { + return; + } + + _enableEvent(EV_REL, axis); + + registered_axis[axis] = true; +} + +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); +} + +std::string InputDevice::toKeyName(uint code) { + return _toEventName(EV_KEY, code); +} + +uint InputDevice::toKeyCode(const std::string& name) { + return _toEventCode(EV_KEY, name); +} + +std::string InputDevice::toAxisName(uint code) { + return _toEventName(EV_REL, code); +} + +uint InputDevice::toAxisCode(const std::string& name) { + return _toEventCode(EV_REL, name); +} + +/* Returns -1 if axis_code is not hi-res */ +int InputDevice::getLowResAxis(const uint axis_code) { + /* Some systems don't have these hi-res axes */ +#ifdef REL_WHEEL_HI_RES + if (axis_code == REL_WHEEL_HI_RES) + return REL_WHEEL; +#endif +#ifdef REL_HWHEEL_HI_RES + if (axis_code == REL_HWHEEL_HI_RES) + return REL_HWHEEL; +#endif + + return -1; +} + +std::string InputDevice::_toEventName(uint type, uint code) { + const char* ret = libevdev_event_code_get_name(type, code); + + if (!ret) + throw InvalidEventCode(code); + + return {ret}; +} + +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::_enableEvent(const uint type, const uint code) { + std::unique_lock lock(_input_mutex); + libevdev_uinput_destroy(ui_device); + + libevdev_enable_event_code(device, type, code, nullptr); + + int err = libevdev_uinput_create_from_device(device, + LIBEVDEV_UINPUT_OPEN_MANAGED, &ui_device); + + if (err != 0) { + libevdev_free(device); + device = nullptr; + ui_device = nullptr; + throw std::system_error(-err, std::generic_category()); + } +} + +void InputDevice::_sendEvent(uint type, uint code, int value) { + std::unique_lock lock(_input_mutex); + libevdev_uinput_write_event(ui_device, type, code, value); + libevdev_uinput_write_event(ui_device, EV_SYN, SYN_REPORT, 0); +} diff --git a/src/logid/InputDevice.h b/src/logid/InputDevice.h index 939d59f..4c41ca6 100644 --- a/src/logid/InputDevice.h +++ b/src/logid/InputDevice.h @@ -1,89 +1,89 @@ -/* - * Copyright 2019-2023 PixlOne - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * 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 -#include -#include - -extern "C" -{ -#include -#include -} - -namespace logid { - class InputDevice { - public: - class InvalidEventCode : public std::exception { - public: - explicit InvalidEventCode(const std::string& name); - - explicit InvalidEventCode(uint code); - - const char* what() const noexcept override; - - private: - const std::string _what; - }; - - explicit InputDevice(const char* name); - - ~InputDevice(); - - void registerKey(uint code); - - void registerAxis(uint axis); - - void moveAxis(uint axis, int movement); - - void pressKey(uint code); - - void releaseKey(uint code); - - static std::string toKeyName(uint code); - - static uint toKeyCode(const std::string& name); - - static std::string toAxisName(uint code); - - static uint toAxisCode(const std::string& name); - - static int getLowResAxis(uint axis_code); - - private: - void _sendEvent(uint type, uint code, int value); - - void _enableEvent(uint type, uint name); - - static std::string _toEventName(uint type, uint code); - - static uint _toEventCode(uint type, const std::string& name); - - bool registered_keys[KEY_CNT]{}; - bool registered_axis[REL_CNT]{}; - libevdev* device; - libevdev_uinput* ui_device{}; - - std::mutex _input_mutex; - }; -} - -#endif //LOGID_INPUTDEVICE_H +/* + * Copyright 2019-2023 PixlOne + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * 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 +#include +#include + +extern "C" +{ +#include +#include +} + +namespace logid { + class InputDevice { + public: + class InvalidEventCode : public std::exception { + public: + explicit InvalidEventCode(const std::string& name); + + explicit InvalidEventCode(uint code); + + const char* what() const noexcept override; + + private: + const std::string _what; + }; + + explicit InputDevice(const char* name); + + ~InputDevice(); + + void registerKey(uint code); + + void registerAxis(uint axis); + + void moveAxis(uint axis, int movement); + + void pressKey(uint code); + + void releaseKey(uint code); + + static std::string toKeyName(uint code); + + static uint toKeyCode(const std::string& name); + + static std::string toAxisName(uint code); + + static uint toAxisCode(const std::string& name); + + static int getLowResAxis(uint axis_code); + + private: + void _sendEvent(uint type, uint code, int value); + + void _enableEvent(uint type, uint name); + + static std::string _toEventName(uint type, uint code); + + static uint _toEventCode(uint type, const std::string& name); + + bool registered_keys[KEY_CNT]{}; + bool registered_axis[REL_CNT]{}; + libevdev* device; + libevdev_uinput* ui_device{}; + + std::mutex _input_mutex; + }; +} + +#endif //LOGID_INPUTDEVICE_H diff --git a/src/logid/Receiver.cpp b/src/logid/Receiver.cpp index 692e1ba..9e4c2bc 100644 --- a/src/logid/Receiver.cpp +++ b/src/logid/Receiver.cpp @@ -1,234 +1,234 @@ -/* - * Copyright 2019-2023 PixlOne - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * 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 - -using namespace logid; -using namespace logid::backend; - -ReceiverNickname::ReceiverNickname( - const std::shared_ptr& manager) : - _nickname(manager->newReceiverNickname()), _manager(manager) { -} - -ReceiverNickname::operator std::string() const { - return std::to_string(_nickname); -} - -ReceiverNickname::~ReceiverNickname() { - if (auto manager = _manager.lock()) { - std::lock_guard lock(manager->_nick_lock); - manager->_receiver_nicknames.erase(_nickname); - } -} - -std::shared_ptr Receiver::make( - const std::string& path, - const std::shared_ptr& manager) { - auto ret = ReceiverMonitor::make(path, manager); - ret->_ipc_node->manage(ret); - return ret; -} - - -Receiver::Receiver(const std::string& path, - const std::shared_ptr& manager) : - hidpp10::ReceiverMonitor(path, manager, - manager->config()->io_timeout.value_or( - defaults::io_timeout)), - _path(path), _manager(manager), _nickname(manager), - _ipc_node(manager->receiversNode()->make_child(_nickname)), - _ipc_interface(_ipc_node->make_interface(this)) { -} - -const Receiver::DeviceList& Receiver::devices() const { - return _devices; -} - -Receiver::~Receiver() noexcept { - if (auto manager = _manager.lock()) { - for (auto& d: _devices) - manager->removeExternalDevice(d.second); - } -} - -void Receiver::addDevice(hidpp::DeviceConnectionEvent event) { - std::unique_lock lock(_devices_change); - - auto manager = _manager.lock(); - if (!manager) { - logPrintf(ERROR, "Orphan Receiver, missing DeviceManager"); - logPrintf(ERROR, "Fatal error, file a bug report. Program will now exit."); - std::terminate(); - } - - try { - // Check if device is ignored before continuing - if (manager->config()->ignore.value_or(std::set()).contains(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) - dev->second->wakeup(); - else - dev->second->sleep(); - return; - } - - if (!event.linkEstablished) - return; - - auto hidpp_device = hidpp::Device::make( - receiver(), event, manager->config()->io_timeout.value_or(defaults::io_timeout)); - - auto version = hidpp_device->version(); - - if (std::get<0>(version) < 2) { - logPrintf(INFO, "Unsupported HID++ 1.0 device on %s:%d connected.", - _path.c_str(), event.index); - return; - } - - hidpp_device.reset(); - - auto device = Device::make(this, event.index, manager); - std::lock_guard manager_lock(manager->mutex()); - _devices.emplace(event.index, device); - manager->addExternalDevice(device); - - } catch (hidpp10::Error& e) { - 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) { - 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); - } -} - -void Receiver::removeDevice(hidpp::DeviceIndex index) { - std::unique_lock lock(_devices_change); - std::unique_lock manager_lock; - if (auto manager = _manager.lock()) - manager_lock = std::unique_lock(manager->mutex()); - auto device = _devices.find(index); - if (device != _devices.end()) { - if (auto manager = _manager.lock()) - manager->removeExternalDevice(device->second); - _devices.erase(device); - } -} - -void Receiver::pairReady(const hidpp10::DeviceDiscoveryEvent& event, - const std::string& passkey) { - std::string type; - switch (event.deviceType) { - case hidpp::DeviceUnknown: - type = "unknown"; - break; - case hidpp::DeviceKeyboard: - type = "keyboard"; - break; - case hidpp::DeviceMouse: - type = "mouse"; - break; - case hidpp::DeviceNumpad: - type = "numpad"; - break; - case hidpp::DevicePresenter: - type = "presenter"; - break; - case hidpp::DeviceTouchpad: - type = "touchpad"; - break; - case hidpp::DeviceTrackball: - type = "trackball"; - break; - } - _ipc_interface->emit_signal("PairReady", event.name, event.pid, type, passkey); -} - -const std::string& Receiver::path() const { - return _path; -} - -std::shared_ptr Receiver::rawReceiver() { - return receiver(); -} - -std::vector> Receiver::pairedDevices() const { - std::vector> ret; - for (int i = hidpp::WirelessDevice1; i <= hidpp::WirelessDevice6; ++i) { - try { - auto index(static_cast(i)); - auto pair_info = receiver()->getPairingInfo(index); - auto extended_pair_info = receiver()->getExtendedPairingInfo(index); - auto name = receiver()->getDeviceName(index); - - ret.emplace_back(i, pair_info.pid, name, extended_pair_info.serialNumber); - } catch (hidpp10::Error& e) { - } - } - - return ret; -} - -void Receiver::startPair(uint8_t timeout) { - _startPair(timeout); -} - -void Receiver::stopPair() { - _stopPair(); -} - -void Receiver::unpair(int device) { - receiver()->disconnect(static_cast(device)); -} - -Receiver::IPC::IPC(Receiver* receiver) : - ipcgull::interface( - SERVICE_ROOT_NAME ".Receiver", - { - {"GetPaired", {receiver, &Receiver::pairedDevices, {"devices"}}}, - {"StartPair", {receiver, &Receiver::startPair, {"timeout"}}}, - {"StopPair", {receiver, &Receiver::stopPair}}, - {"Unpair", {receiver, &Receiver::unpair, {"device"}}} - }, - { - {"Bolt", ipcgull::property(ipcgull::property_readable, - receiver->receiver()->bolt())} - }, { - {"PairReady", - ipcgull::signal::make_signal( - {"name", "pid", "type", "passkey"}) - } - }) { -} +/* + * Copyright 2019-2023 PixlOne + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * 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 + +using namespace logid; +using namespace logid::backend; + +ReceiverNickname::ReceiverNickname( + const std::shared_ptr& manager) : + _nickname(manager->newReceiverNickname()), _manager(manager) { +} + +ReceiverNickname::operator std::string() const { + return std::to_string(_nickname); +} + +ReceiverNickname::~ReceiverNickname() { + if (auto manager = _manager.lock()) { + std::lock_guard lock(manager->_nick_lock); + manager->_receiver_nicknames.erase(_nickname); + } +} + +std::shared_ptr Receiver::make( + const std::string& path, + const std::shared_ptr& manager) { + auto ret = ReceiverMonitor::make(path, manager); + ret->_ipc_node->manage(ret); + return ret; +} + + +Receiver::Receiver(const std::string& path, + const std::shared_ptr& manager) : + hidpp10::ReceiverMonitor(path, manager, + manager->config()->io_timeout.value_or( + defaults::io_timeout)), + _path(path), _manager(manager), _nickname(manager), + _ipc_node(manager->receiversNode()->make_child(_nickname)), + _ipc_interface(_ipc_node->make_interface(this)) { +} + +const Receiver::DeviceList& Receiver::devices() const { + return _devices; +} + +Receiver::~Receiver() noexcept { + if (auto manager = _manager.lock()) { + for (auto& d: _devices) + manager->removeExternalDevice(d.second); + } +} + +void Receiver::addDevice(hidpp::DeviceConnectionEvent event) { + std::unique_lock lock(_devices_change); + + auto manager = _manager.lock(); + if (!manager) { + logPrintf(ERROR, "Orphan Receiver, missing DeviceManager"); + logPrintf(ERROR, "Fatal error, file a bug report. Program will now exit."); + std::terminate(); + } + + try { + // Check if device is ignored before continuing + if (manager->config()->ignore.value_or(std::set()).contains(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) + dev->second->wakeup(); + else + dev->second->sleep(); + return; + } + + if (!event.linkEstablished) + return; + + auto hidpp_device = hidpp::Device::make( + receiver(), event, manager->config()->io_timeout.value_or(defaults::io_timeout)); + + auto version = hidpp_device->version(); + + if (std::get<0>(version) < 2) { + logPrintf(INFO, "Unsupported HID++ 1.0 device on %s:%d connected.", + _path.c_str(), event.index); + return; + } + + hidpp_device.reset(); + + auto device = Device::make(this, event.index, manager); + std::lock_guard manager_lock(manager->mutex()); + _devices.emplace(event.index, device); + manager->addExternalDevice(device); + + } catch (hidpp10::Error& e) { + 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) { + 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); + } +} + +void Receiver::removeDevice(hidpp::DeviceIndex index) { + std::unique_lock lock(_devices_change); + std::unique_lock manager_lock; + if (auto manager = _manager.lock()) + manager_lock = std::unique_lock(manager->mutex()); + auto device = _devices.find(index); + if (device != _devices.end()) { + if (auto manager = _manager.lock()) + manager->removeExternalDevice(device->second); + _devices.erase(device); + } +} + +void Receiver::pairReady(const hidpp10::DeviceDiscoveryEvent& event, + const std::string& passkey) { + std::string type; + switch (event.deviceType) { + case hidpp::DeviceUnknown: + type = "unknown"; + break; + case hidpp::DeviceKeyboard: + type = "keyboard"; + break; + case hidpp::DeviceMouse: + type = "mouse"; + break; + case hidpp::DeviceNumpad: + type = "numpad"; + break; + case hidpp::DevicePresenter: + type = "presenter"; + break; + case hidpp::DeviceTouchpad: + type = "touchpad"; + break; + case hidpp::DeviceTrackball: + type = "trackball"; + break; + } + _ipc_interface->emit_signal("PairReady", event.name, event.pid, type, passkey); +} + +const std::string& Receiver::path() const { + return _path; +} + +std::shared_ptr Receiver::rawReceiver() { + return receiver(); +} + +std::vector> Receiver::pairedDevices() const { + std::vector> ret; + for (int i = hidpp::WirelessDevice1; i <= hidpp::WirelessDevice6; ++i) { + try { + auto index(static_cast(i)); + auto pair_info = receiver()->getPairingInfo(index); + auto extended_pair_info = receiver()->getExtendedPairingInfo(index); + auto name = receiver()->getDeviceName(index); + + ret.emplace_back(i, pair_info.pid, name, extended_pair_info.serialNumber); + } catch (hidpp10::Error& e) { + } + } + + return ret; +} + +void Receiver::startPair(uint8_t timeout) { + _startPair(timeout); +} + +void Receiver::stopPair() { + _stopPair(); +} + +void Receiver::unpair(int device) { + receiver()->disconnect(static_cast(device)); +} + +Receiver::IPC::IPC(Receiver* receiver) : + ipcgull::interface( + SERVICE_ROOT_NAME ".Receiver", + { + {"GetPaired", {receiver, &Receiver::pairedDevices, {"devices"}}}, + {"StartPair", {receiver, &Receiver::startPair, {"timeout"}}}, + {"StopPair", {receiver, &Receiver::stopPair}}, + {"Unpair", {receiver, &Receiver::unpair, {"device"}}} + }, + { + {"Bolt", ipcgull::property(ipcgull::property_readable, + receiver->receiver()->bolt())} + }, { + {"PairReady", + ipcgull::signal::make_signal( + {"name", "pid", "type", "passkey"}) + } + }) { +} diff --git a/src/logid/Receiver.h b/src/logid/Receiver.h index 89e7e68..57802b6 100644 --- a/src/logid/Receiver.h +++ b/src/logid/Receiver.h @@ -1,100 +1,100 @@ -/* - * Copyright 2019-2023 PixlOne - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * 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 - -#include -#include -#include - -namespace logid { - class ReceiverNickname { - public: - explicit ReceiverNickname(const std::shared_ptr& manager); - - ReceiverNickname() = delete; - - ReceiverNickname(const ReceiverNickname&) = delete; - - ~ReceiverNickname(); - - operator std::string() const; - - private: - const int _nickname; - const std::weak_ptr _manager; - }; - - class Receiver : public backend::hidpp10::ReceiverMonitor, - public ipcgull::object { - public: - typedef std::map> - DeviceList; - - ~Receiver() noexcept override; - - static std::shared_ptr make( - const std::string& path, - const std::shared_ptr& manager); - - [[nodiscard]] const std::string& path() const; - - std::shared_ptr rawReceiver(); - - [[nodiscard]] const DeviceList& devices() const; - - [[nodiscard]] std::vector> - pairedDevices() const; - - void startPair(uint8_t timeout); - - void stopPair(); - - void unpair(int device); - - protected: - Receiver(const std::string& path, - const std::shared_ptr& manager); - - void addDevice(backend::hidpp::DeviceConnectionEvent event) override; - - void removeDevice(backend::hidpp::DeviceIndex index) override; - - void pairReady(const backend::hidpp10::DeviceDiscoveryEvent& event, - const std::string& passkey) override; - - private: - std::mutex _devices_change; - DeviceList _devices; - std::string _path; - std::weak_ptr _manager; - - const ReceiverNickname _nickname; - std::shared_ptr _ipc_node; - - class IPC : public ipcgull::interface { - public: - explicit IPC(Receiver* receiver); - }; - - std::shared_ptr _ipc_interface; - }; -} - +/* + * Copyright 2019-2023 PixlOne + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * 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 + +#include +#include +#include + +namespace logid { + class ReceiverNickname { + public: + explicit ReceiverNickname(const std::shared_ptr& manager); + + ReceiverNickname() = delete; + + ReceiverNickname(const ReceiverNickname&) = delete; + + ~ReceiverNickname(); + + operator std::string() const; + + private: + const int _nickname; + const std::weak_ptr _manager; + }; + + class Receiver : public backend::hidpp10::ReceiverMonitor, + public ipcgull::object { + public: + typedef std::map> + DeviceList; + + ~Receiver() noexcept override; + + static std::shared_ptr make( + const std::string& path, + const std::shared_ptr& manager); + + [[nodiscard]] const std::string& path() const; + + std::shared_ptr rawReceiver(); + + [[nodiscard]] const DeviceList& devices() const; + + [[nodiscard]] std::vector> + pairedDevices() const; + + void startPair(uint8_t timeout); + + void stopPair(); + + void unpair(int device); + + protected: + Receiver(const std::string& path, + const std::shared_ptr& manager); + + void addDevice(backend::hidpp::DeviceConnectionEvent event) override; + + void removeDevice(backend::hidpp::DeviceIndex index) override; + + void pairReady(const backend::hidpp10::DeviceDiscoveryEvent& event, + const std::string& passkey) override; + + private: + std::mutex _devices_change; + DeviceList _devices; + std::string _path; + std::weak_ptr _manager; + + const ReceiverNickname _nickname; + std::shared_ptr _ipc_node; + + class IPC : public ipcgull::interface { + public: + explicit IPC(Receiver* receiver); + }; + + std::shared_ptr _ipc_interface; + }; +} + #endif //LOGID_RECEIVER_H \ No newline at end of file diff --git a/src/logid/actions/Action.cpp b/src/logid/actions/Action.cpp index 317a2dc..ed2b681 100644 --- a/src/logid/actions/Action.cpp +++ b/src/logid/actions/Action.cpp @@ -1,143 +1,143 @@ -/* - * Copyright 2019-2023 PixlOne - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * 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 -#include -#include - -using namespace logid; -using namespace logid::actions; - -namespace logid::actions { - template - struct action_type { - typedef typename T::action type; - }; - - template - struct action_type : action_type { - }; - - template - struct action_type : action_type { - }; - - template - std::shared_ptr _makeAction( - Device* device, T& action, - const std::shared_ptr& parent) { - return parent->make_interface::type>( - device, std::forward(action), parent); - } - - template - std::shared_ptr _makeAction( - Device* device, const std::string& name, - std::optional& config, - const std::shared_ptr& parent) { - if (name == ChangeDPI::interface_name) { - config = config::ChangeDPI(); - } else if (name == CycleDPI::interface_name) { - config = config::CycleDPI(); - } else if (name == KeypressAction::interface_name) { - config = config::KeypressAction(); - } else if (name == NullAction::interface_name) { - config = config::NoAction(); - } else if (name == ChangeHostAction::interface_name) { - config = config::ChangeHost(); - } else if (name == ToggleHiresScroll::interface_name) { - config = config::ToggleHiresScroll(); - } else if (name == ToggleSmartShift::interface_name) { - config = config::ToggleSmartShift(); - } else if (name == ChangeProfile::interface_name) { - config = config::ChangeProfile(); - } else if (name == "Default") { - config.reset(); - return nullptr; - } else { - throw InvalidAction(name); - } - - return Action::makeAction(device, config.value(), parent); - } -} - -std::shared_ptr Action::makeAction( - Device* device, const std::string& name, - std::optional& config, - const std::shared_ptr& parent) { - auto ret = _makeAction(device, name, config, parent); - if (ret) - ret->_self = ret; - return ret; -} - -std::shared_ptr Action::makeAction( - Device* device, const std::string& name, - std::optional& config, - const std::shared_ptr& parent) { - try { - auto ret = _makeAction(device, name, config, parent); - if (ret) - ret->_self = ret; - return ret; - } catch (actions::InvalidAction& e) { - if (name == GestureAction::interface_name) { - config = config::GestureAction(); - return makeAction(device, config.value(), parent); - } - throw; - } -} - -std::shared_ptr Action::makeAction( - Device* device, config::BasicAction& action, - const std::shared_ptr& parent) { - std::shared_ptr ret; - std::visit([&device, &ret, &parent](auto&& x) { - ret = _makeAction(device, x, parent); - }, action); - if (ret) - ret->_self = ret; - return ret; -} - -std::shared_ptr Action::makeAction( - Device* device, config::Action& action, - const std::shared_ptr& parent) { - std::shared_ptr ret; - std::visit([&device, &ret, &parent](auto&& x) { - ret = _makeAction(device, x, parent); - }, action); - if (ret) - ret->_self = ret; - return ret; -} - -Action::Action(Device* device, const std::string& name, tables t) : - ipcgull::interface(SERVICE_ROOT_NAME ".Action." + name, std::move(t)), - _device(device), _pressed(false) { -} +/* + * Copyright 2019-2023 PixlOne + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * 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 +#include +#include + +using namespace logid; +using namespace logid::actions; + +namespace logid::actions { + template + struct action_type { + typedef typename T::action type; + }; + + template + struct action_type : action_type { + }; + + template + struct action_type : action_type { + }; + + template + std::shared_ptr _makeAction( + Device* device, T& action, + const std::shared_ptr& parent) { + return parent->make_interface::type>( + device, std::forward(action), parent); + } + + template + std::shared_ptr _makeAction( + Device* device, const std::string& name, + std::optional& config, + const std::shared_ptr& parent) { + if (name == ChangeDPI::interface_name) { + config = config::ChangeDPI(); + } else if (name == CycleDPI::interface_name) { + config = config::CycleDPI(); + } else if (name == KeypressAction::interface_name) { + config = config::KeypressAction(); + } else if (name == NullAction::interface_name) { + config = config::NoAction(); + } else if (name == ChangeHostAction::interface_name) { + config = config::ChangeHost(); + } else if (name == ToggleHiresScroll::interface_name) { + config = config::ToggleHiresScroll(); + } else if (name == ToggleSmartShift::interface_name) { + config = config::ToggleSmartShift(); + } else if (name == ChangeProfile::interface_name) { + config = config::ChangeProfile(); + } else if (name == "Default") { + config.reset(); + return nullptr; + } else { + throw InvalidAction(name); + } + + return Action::makeAction(device, config.value(), parent); + } +} + +std::shared_ptr Action::makeAction( + Device* device, const std::string& name, + std::optional& config, + const std::shared_ptr& parent) { + auto ret = _makeAction(device, name, config, parent); + if (ret) + ret->_self = ret; + return ret; +} + +std::shared_ptr Action::makeAction( + Device* device, const std::string& name, + std::optional& config, + const std::shared_ptr& parent) { + try { + auto ret = _makeAction(device, name, config, parent); + if (ret) + ret->_self = ret; + return ret; + } catch (actions::InvalidAction& e) { + if (name == GestureAction::interface_name) { + config = config::GestureAction(); + return makeAction(device, config.value(), parent); + } + throw; + } +} + +std::shared_ptr Action::makeAction( + Device* device, config::BasicAction& action, + const std::shared_ptr& parent) { + std::shared_ptr ret; + std::visit([&device, &ret, &parent](auto&& x) { + ret = _makeAction(device, x, parent); + }, action); + if (ret) + ret->_self = ret; + return ret; +} + +std::shared_ptr Action::makeAction( + Device* device, config::Action& action, + const std::shared_ptr& parent) { + std::shared_ptr ret; + std::visit([&device, &ret, &parent](auto&& x) { + ret = _makeAction(device, x, parent); + }, action); + if (ret) + ret->_self = ret; + return ret; +} + +Action::Action(Device* device, const std::string& name, tables t) : + ipcgull::interface(SERVICE_ROOT_NAME ".Action." + name, std::move(t)), + _device(device), _pressed(false) { +} diff --git a/src/logid/actions/Action.h b/src/logid/actions/Action.h index f00979b..17fcbfd 100644 --- a/src/logid/actions/Action.h +++ b/src/logid/actions/Action.h @@ -1,99 +1,99 @@ -/* - * Copyright 2019-2023 PixlOne - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * 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 -#include -#include -#include -#include - -namespace logid { - class Device; -} - -namespace logid::actions { - class InvalidAction : public std::exception { - public: - InvalidAction() = default; - - InvalidAction(std::string action) : _action(std::move(action)) {} - - [[nodiscard]] const char* what() const noexcept override { - return _action.c_str(); - } - - private: - std::string _action; - }; - - class Action : public ipcgull::interface { - public: - static std::shared_ptr makeAction( - Device* device, const std::string& name, - std::optional& config, - const std::shared_ptr& parent); - - static std::shared_ptr makeAction( - Device* device, const std::string& name, - std::optional& config, - const std::shared_ptr& parent); - - static std::shared_ptr makeAction( - Device* device, config::BasicAction& action, - const std::shared_ptr& parent); - - static std::shared_ptr makeAction( - Device* device, config::Action& action, - const std::shared_ptr& parent); - - virtual void press() = 0; - - virtual void release() = 0; - - virtual void move([[maybe_unused]] int16_t x, [[maybe_unused]] int16_t y) { } - - virtual bool pressed() { - return _pressed; - } - - [[nodiscard]] virtual uint8_t reprogFlags() const = 0; - - virtual ~Action() = default; - - protected: - Action(Device* device, const std::string& name, tables t = {}); - - Device* _device; - std::atomic _pressed; - mutable std::shared_mutex _config_mutex; - - template - [[nodiscard]] std::weak_ptr self() const { - return std::dynamic_pointer_cast(_self.lock()); - } - - private: - std::weak_ptr _self; - }; -} - -#endif //LOGID_ACTION_H +/* + * Copyright 2019-2023 PixlOne + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * 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 +#include +#include +#include +#include + +namespace logid { + class Device; +} + +namespace logid::actions { + class InvalidAction : public std::exception { + public: + InvalidAction() = default; + + InvalidAction(std::string action) : _action(std::move(action)) {} + + [[nodiscard]] const char* what() const noexcept override { + return _action.c_str(); + } + + private: + std::string _action; + }; + + class Action : public ipcgull::interface { + public: + static std::shared_ptr makeAction( + Device* device, const std::string& name, + std::optional& config, + const std::shared_ptr& parent); + + static std::shared_ptr makeAction( + Device* device, const std::string& name, + std::optional& config, + const std::shared_ptr& parent); + + static std::shared_ptr makeAction( + Device* device, config::BasicAction& action, + const std::shared_ptr& parent); + + static std::shared_ptr makeAction( + Device* device, config::Action& action, + const std::shared_ptr& parent); + + virtual void press() = 0; + + virtual void release() = 0; + + virtual void move([[maybe_unused]] int16_t x, [[maybe_unused]] int16_t y) { } + + virtual bool pressed() { + return _pressed; + } + + [[nodiscard]] virtual uint8_t reprogFlags() const = 0; + + virtual ~Action() = default; + + protected: + Action(Device* device, const std::string& name, tables t = {}); + + Device* _device; + std::atomic _pressed; + mutable std::shared_mutex _config_mutex; + + template + [[nodiscard]] std::weak_ptr self() const { + return std::dynamic_pointer_cast(_self.lock()); + } + + private: + std::weak_ptr _self; + }; +} + +#endif //LOGID_ACTION_H diff --git a/src/logid/actions/ChangeDPI.cpp b/src/logid/actions/ChangeDPI.cpp index 4ac58c0..a61e01f 100644 --- a/src/logid/actions/ChangeDPI.cpp +++ b/src/logid/actions/ChangeDPI.cpp @@ -1,94 +1,94 @@ -/* - * Copyright 2019-2023 PixlOne - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * 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 - -using namespace logid::actions; - -const char* ChangeDPI::interface_name = "ChangeDPI"; - -ChangeDPI::ChangeDPI( - Device* device, config::ChangeDPI& config, - [[maybe_unused]] const std::shared_ptr& parent) : - Action(device, interface_name, { - { - {"GetConfig", {this, &ChangeDPI::getConfig, {"change", "sensor"}}}, - {"SetChange", {this, &ChangeDPI::setChange, {"change"}}}, - {"SetSensor", {this, &ChangeDPI::setSensor, {"sensor", "reset"}}}, - }, - {}, - {}}), _config(config) { - _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; - std::shared_lock lock(_config_mutex); - if (_dpi && _config.inc.has_value()) { - run_task([self_weak = self(), - sensor = _config.sensor.value_or(0), inc = _config.inc.value()] { - if (auto self = self_weak.lock()) { - try { - uint16_t last_dpi = self->_dpi->getDPI(sensor); - self->_dpi->setDPI(last_dpi + inc, 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", - self->_device->hidpp20().devicePath().c_str(), - self->_device->hidpp20().deviceIndex(), sensor); - else - throw e; - } - } - }); - } -} - -void ChangeDPI::release() { - _pressed = false; -} - -uint8_t ChangeDPI::reprogFlags() const { - return backend::hidpp20::ReprogControls::TemporaryDiverted; -} - -std::tuple ChangeDPI::getConfig() const { - std::shared_lock lock(_config_mutex); - return {_config.inc.value_or(0), _config.sensor.value_or(0)}; -} - -void ChangeDPI::setChange(int16_t change) { - std::unique_lock lock(_config_mutex); - _config.inc = change; -} - -void ChangeDPI::setSensor(uint8_t sensor, bool reset) { - std::unique_lock lock(_config_mutex); - if (reset) { - _config.sensor.reset(); - } else { - _config.sensor = sensor; - } -} +/* + * Copyright 2019-2023 PixlOne + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * 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 + +using namespace logid::actions; + +const char* ChangeDPI::interface_name = "ChangeDPI"; + +ChangeDPI::ChangeDPI( + Device* device, config::ChangeDPI& config, + [[maybe_unused]] const std::shared_ptr& parent) : + Action(device, interface_name, { + { + {"GetConfig", {this, &ChangeDPI::getConfig, {"change", "sensor"}}}, + {"SetChange", {this, &ChangeDPI::setChange, {"change"}}}, + {"SetSensor", {this, &ChangeDPI::setSensor, {"sensor", "reset"}}}, + }, + {}, + {}}), _config(config) { + _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; + std::shared_lock lock(_config_mutex); + if (_dpi && _config.inc.has_value()) { + run_task([self_weak = self(), + sensor = _config.sensor.value_or(0), inc = _config.inc.value()] { + if (auto self = self_weak.lock()) { + try { + uint16_t last_dpi = self->_dpi->getDPI(sensor); + self->_dpi->setDPI(last_dpi + inc, 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", + self->_device->hidpp20().devicePath().c_str(), + self->_device->hidpp20().deviceIndex(), sensor); + else + throw e; + } + } + }); + } +} + +void ChangeDPI::release() { + _pressed = false; +} + +uint8_t ChangeDPI::reprogFlags() const { + return backend::hidpp20::ReprogControls::TemporaryDiverted; +} + +std::tuple ChangeDPI::getConfig() const { + std::shared_lock lock(_config_mutex); + return {_config.inc.value_or(0), _config.sensor.value_or(0)}; +} + +void ChangeDPI::setChange(int16_t change) { + std::unique_lock lock(_config_mutex); + _config.inc = change; +} + +void ChangeDPI::setSensor(uint8_t sensor, bool reset) { + std::unique_lock lock(_config_mutex); + if (reset) { + _config.sensor.reset(); + } else { + _config.sensor = sensor; + } +} diff --git a/src/logid/actions/ChangeDPI.h b/src/logid/actions/ChangeDPI.h index 2a3a23f..0bd47dd 100644 --- a/src/logid/actions/ChangeDPI.h +++ b/src/logid/actions/ChangeDPI.h @@ -1,50 +1,50 @@ -/* - * Copyright 2019-2023 PixlOne - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * 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 - -namespace logid::actions { - class ChangeDPI : public Action { - public: - static const char* interface_name; - - ChangeDPI(Device* device, config::ChangeDPI& setting, - const std::shared_ptr& parent); - - void press() final; - - void release() final; - - [[nodiscard]] std::tuple getConfig() const; - - void setChange(int16_t change); - - void setSensor(uint8_t sensor, bool reset); - - [[nodiscard]] uint8_t reprogFlags() const final; - - protected: - config::ChangeDPI& _config; - std::shared_ptr _dpi; - }; -} - +/* + * Copyright 2019-2023 PixlOne + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * 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 + +namespace logid::actions { + class ChangeDPI : public Action { + public: + static const char* interface_name; + + ChangeDPI(Device* device, config::ChangeDPI& setting, + const std::shared_ptr& parent); + + void press() final; + + void release() final; + + [[nodiscard]] std::tuple getConfig() const; + + void setChange(int16_t change); + + void setSensor(uint8_t sensor, bool reset); + + [[nodiscard]] uint8_t reprogFlags() const final; + + protected: + config::ChangeDPI& _config; + std::shared_ptr _dpi; + }; +} + #endif //LOGID_ACTION_CHANGEDPI_H \ No newline at end of file diff --git a/src/logid/actions/ChangeHostAction.cpp b/src/logid/actions/ChangeHostAction.cpp index 0da9138..6a73dd6 100644 --- a/src/logid/actions/ChangeHostAction.cpp +++ b/src/logid/actions/ChangeHostAction.cpp @@ -1,112 +1,112 @@ -/* - * Copyright 2019-2023 PixlOne - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * 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 - -using namespace logid::actions; -using namespace logid::backend; - -const char* ChangeHostAction::interface_name = "ChangeHost"; - -ChangeHostAction::ChangeHostAction( - Device* device, config::ChangeHost& config, - [[maybe_unused]] const std::shared_ptr& parent) - : Action(device, interface_name, { - { - {"GetHost", {this, &ChangeHostAction::getHost, {"host"}}}, - {"SetHost", {this, &ChangeHostAction::setHost, {"host"}}} - }, - {}, - {} -}), _config(config) { - if (_config.host.has_value()) { - if (std::holds_alternative(_config.host.value())) { - auto& host = std::get(_config.host.value()); - std::transform(host.begin(), host.end(), - host.begin(), ::tolower); - } - } - try { - _change_host = std::make_shared(&device->hidpp20()); - } catch (hidpp20::UnsupportedFeature& e) { - logPrintf(WARN, "%s:%d: ChangeHost feature not supported, " - "ChangeHostAction will not work.", device->hidpp20() - .devicePath().c_str(), device->hidpp20().deviceIndex()); - } -} - -std::string ChangeHostAction::getHost() const { - std::shared_lock lock(_config_mutex); - if (_config.host.has_value()) { - if (std::holds_alternative(_config.host.value())) - return std::get(_config.host.value()); - else - return std::to_string(std::get(_config.host.value())); - } else { - return ""; - } -} - -void ChangeHostAction::setHost(std::string host) { - std::transform(host.begin(), host.end(), - host.begin(), ::tolower); - std::unique_lock lock(_config_mutex); - if (host == "next" || host == "prev" || host == "previous") { - _config.host = std::move(host); - } else { - _config.host = std::stoi(host); - } -} - -void ChangeHostAction::press() { - // Do nothing, wait until release -} - -void ChangeHostAction::release() { - std::shared_lock lock(_config_mutex); - if (_change_host && _config.host.has_value()) { - run_task([self_weak = self(), host = _config.host.value()] { - if (auto self = self_weak.lock()) { - auto host_info = self->_change_host->getHostInfo(); - int next_host; - if (std::holds_alternative(host)) { - const auto& host_str = std::get(host); - if (host_str == "next") - next_host = host_info.currentHost + 1; - else if (host_str == "prev" || host_str == "previous") - next_host = host_info.currentHost - 1; - else - next_host = host_info.currentHost; - } else { - next_host = std::get(host) - 1; - } - next_host %= host_info.hostCount; - if (next_host != host_info.currentHost) - self->_change_host->setHost(next_host); - } - }); - } -} - -uint8_t ChangeHostAction::reprogFlags() const { - return hidpp20::ReprogControls::TemporaryDiverted; -} +/* + * Copyright 2019-2023 PixlOne + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * 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 + +using namespace logid::actions; +using namespace logid::backend; + +const char* ChangeHostAction::interface_name = "ChangeHost"; + +ChangeHostAction::ChangeHostAction( + Device* device, config::ChangeHost& config, + [[maybe_unused]] const std::shared_ptr& parent) + : Action(device, interface_name, { + { + {"GetHost", {this, &ChangeHostAction::getHost, {"host"}}}, + {"SetHost", {this, &ChangeHostAction::setHost, {"host"}}} + }, + {}, + {} +}), _config(config) { + if (_config.host.has_value()) { + if (std::holds_alternative(_config.host.value())) { + auto& host = std::get(_config.host.value()); + std::transform(host.begin(), host.end(), + host.begin(), ::tolower); + } + } + try { + _change_host = std::make_shared(&device->hidpp20()); + } catch (hidpp20::UnsupportedFeature& e) { + logPrintf(WARN, "%s:%d: ChangeHost feature not supported, " + "ChangeHostAction will not work.", device->hidpp20() + .devicePath().c_str(), device->hidpp20().deviceIndex()); + } +} + +std::string ChangeHostAction::getHost() const { + std::shared_lock lock(_config_mutex); + if (_config.host.has_value()) { + if (std::holds_alternative(_config.host.value())) + return std::get(_config.host.value()); + else + return std::to_string(std::get(_config.host.value())); + } else { + return ""; + } +} + +void ChangeHostAction::setHost(std::string host) { + std::transform(host.begin(), host.end(), + host.begin(), ::tolower); + std::unique_lock lock(_config_mutex); + if (host == "next" || host == "prev" || host == "previous") { + _config.host = std::move(host); + } else { + _config.host = std::stoi(host); + } +} + +void ChangeHostAction::press() { + // Do nothing, wait until release +} + +void ChangeHostAction::release() { + std::shared_lock lock(_config_mutex); + if (_change_host && _config.host.has_value()) { + run_task([self_weak = self(), host = _config.host.value()] { + if (auto self = self_weak.lock()) { + auto host_info = self->_change_host->getHostInfo(); + int next_host; + if (std::holds_alternative(host)) { + const auto& host_str = std::get(host); + if (host_str == "next") + next_host = host_info.currentHost + 1; + else if (host_str == "prev" || host_str == "previous") + next_host = host_info.currentHost - 1; + else + next_host = host_info.currentHost; + } else { + next_host = std::get(host) - 1; + } + next_host %= host_info.hostCount; + if (next_host != host_info.currentHost) + self->_change_host->setHost(next_host); + } + }); + } +} + +uint8_t ChangeHostAction::reprogFlags() const { + return hidpp20::ReprogControls::TemporaryDiverted; +} diff --git a/src/logid/actions/ChangeHostAction.h b/src/logid/actions/ChangeHostAction.h index 2d13384..b4fd483 100644 --- a/src/logid/actions/ChangeHostAction.h +++ b/src/logid/actions/ChangeHostAction.h @@ -1,48 +1,48 @@ -/* - * Copyright 2019-2023 PixlOne - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * 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_CHANGEHOSTACTION_H -#define LOGID_ACTION_CHANGEHOSTACTION_H - -#include -#include - -namespace logid::actions { - class ChangeHostAction : public Action { - public: - static const char* interface_name; - - ChangeHostAction(Device* device, config::ChangeHost& config, - const std::shared_ptr& parent); - - void press() final; - - void release() final; - - [[nodiscard]] std::string getHost() const; - - void setHost(std::string host); - - [[nodiscard]] uint8_t reprogFlags() const final; - - protected: - std::shared_ptr _change_host; - config::ChangeHost& _config; - }; -} - -#endif //LOGID_ACTION_CHANGEHOSTACTION_H +/* + * Copyright 2019-2023 PixlOne + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * 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_CHANGEHOSTACTION_H +#define LOGID_ACTION_CHANGEHOSTACTION_H + +#include +#include + +namespace logid::actions { + class ChangeHostAction : public Action { + public: + static const char* interface_name; + + ChangeHostAction(Device* device, config::ChangeHost& config, + const std::shared_ptr& parent); + + void press() final; + + void release() final; + + [[nodiscard]] std::string getHost() const; + + void setHost(std::string host); + + [[nodiscard]] uint8_t reprogFlags() const final; + + protected: + std::shared_ptr _change_host; + config::ChangeHost& _config; + }; +} + +#endif //LOGID_ACTION_CHANGEHOSTACTION_H diff --git a/src/logid/actions/ChangeProfile.cpp b/src/logid/actions/ChangeProfile.cpp index 4e0f7b0..83f9e47 100644 --- a/src/logid/actions/ChangeProfile.cpp +++ b/src/logid/actions/ChangeProfile.cpp @@ -1,67 +1,67 @@ -/* - * Copyright 2019-2023 PixlOne - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * 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 - -using namespace logid; -using namespace logid::actions; - -const char* ChangeProfile::interface_name = "ChangeProfile"; - -ChangeProfile::ChangeProfile(Device* device, config::ChangeProfile& config, - [[maybe_unused]] const std::shared_ptr& parent) : - Action(device, interface_name, { - { - {"GetProfile", {this, &ChangeProfile::getProfile, {"profile"}}}, - {"SetProfile", {this, &ChangeProfile::setProfile, {"profile"}}} - }, - {}, - {} - }), _config(config) { -} - -void ChangeProfile::press() { -} - -void ChangeProfile::release() { - std::shared_lock lock(_config_mutex); - if (_config.profile.has_value()) - _device->setProfileDelayed(_config.profile.value()); -} - -uint8_t ChangeProfile::reprogFlags() const { - return backend::hidpp20::ReprogControls::TemporaryDiverted; -} - -std::string ChangeProfile::getProfile() { - std::shared_lock lock(_config_mutex); - if (_config.profile.has_value()) - return _config.profile.value(); - else - return ""; -} - -void ChangeProfile::setProfile(std::string profile) { - std::unique_lock lock(_config_mutex); - - if (profile.empty()) - _config.profile->clear(); - else - _config.profile = std::move(profile); -} +/* + * Copyright 2019-2023 PixlOne + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * 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 + +using namespace logid; +using namespace logid::actions; + +const char* ChangeProfile::interface_name = "ChangeProfile"; + +ChangeProfile::ChangeProfile(Device* device, config::ChangeProfile& config, + [[maybe_unused]] const std::shared_ptr& parent) : + Action(device, interface_name, { + { + {"GetProfile", {this, &ChangeProfile::getProfile, {"profile"}}}, + {"SetProfile", {this, &ChangeProfile::setProfile, {"profile"}}} + }, + {}, + {} + }), _config(config) { +} + +void ChangeProfile::press() { +} + +void ChangeProfile::release() { + std::shared_lock lock(_config_mutex); + if (_config.profile.has_value()) + _device->setProfileDelayed(_config.profile.value()); +} + +uint8_t ChangeProfile::reprogFlags() const { + return backend::hidpp20::ReprogControls::TemporaryDiverted; +} + +std::string ChangeProfile::getProfile() { + std::shared_lock lock(_config_mutex); + if (_config.profile.has_value()) + return _config.profile.value(); + else + return ""; +} + +void ChangeProfile::setProfile(std::string profile) { + std::unique_lock lock(_config_mutex); + + if (profile.empty()) + _config.profile->clear(); + else + _config.profile = std::move(profile); +} diff --git a/src/logid/actions/ChangeProfile.h b/src/logid/actions/ChangeProfile.h index 0cbdc91..6b1640d 100644 --- a/src/logid/actions/ChangeProfile.h +++ b/src/logid/actions/ChangeProfile.h @@ -1,47 +1,47 @@ -/* - * Copyright 2019-2023 PixlOne - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * 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_CHANGEPROFILE_H -#define LOGID_CHANGEPROFILE_H - -#include - -namespace logid::actions { - class ChangeProfile : public Action { - public: - static const char* interface_name; - - ChangeProfile(Device* device, config::ChangeProfile& setting, - const std::shared_ptr& parent); - - void press() final; - - void release() final; - - [[nodiscard]] uint8_t reprogFlags() const final; - - std::string getProfile(); - - void setProfile(std::string profile); - - private: - config::ChangeProfile& _config; - }; -} - - -#endif //LOGID_CHANGEPROFILE_H +/* + * Copyright 2019-2023 PixlOne + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * 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_CHANGEPROFILE_H +#define LOGID_CHANGEPROFILE_H + +#include + +namespace logid::actions { + class ChangeProfile : public Action { + public: + static const char* interface_name; + + ChangeProfile(Device* device, config::ChangeProfile& setting, + const std::shared_ptr& parent); + + void press() final; + + void release() final; + + [[nodiscard]] uint8_t reprogFlags() const final; + + std::string getProfile(); + + void setProfile(std::string profile); + + private: + config::ChangeProfile& _config; + }; +} + + +#endif //LOGID_CHANGEPROFILE_H diff --git a/src/logid/actions/CycleDPI.cpp b/src/logid/actions/CycleDPI.cpp index ee23252..7603e10 100644 --- a/src/logid/actions/CycleDPI.cpp +++ b/src/logid/actions/CycleDPI.cpp @@ -1,98 +1,98 @@ -/* - * Copyright 2019-2023 PixlOne - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * 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 - -using namespace logid::actions; - -const char* CycleDPI::interface_name = "CycleDPI"; - -CycleDPI::CycleDPI(Device* device, config::CycleDPI& config, - [[maybe_unused]] const std::shared_ptr& parent) : - Action(device, interface_name, { - { - {"GetDPIs", {this, &CycleDPI::getDPIs, {"dpis"}}}, - {"SetDPIs", {this, &CycleDPI::setDPIs, {"dpis"}}} - }, - {}, - {} - }), - _config(config) { - _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()); - - if (_config.dpis.has_value()) { - _current_dpi = _config.dpis.value().begin(); - } -} - -std::vector CycleDPI::getDPIs() const { - std::shared_lock lock(_config_mutex); - auto dpis = _config.dpis.value_or(std::list()); - return {dpis.begin(), dpis.end()}; -} - -void CycleDPI::setDPIs(const std::vector& dpis) { - std::unique_lock lock(_config_mutex); - std::lock_guard dpi_lock(_dpi_mutex); - _config.dpis.emplace(dpis.begin(), dpis.end()); - _current_dpi = _config.dpis->cbegin(); -} - -void CycleDPI::press() { - _pressed = true; - std::shared_lock lock(_config_mutex); - std::lock_guard dpi_lock(_dpi_mutex); - if (_dpi && _config.dpis.has_value() && _config.dpis.value().empty()) { - ++_current_dpi; - if (_current_dpi == _config.dpis.value().end()) - _current_dpi = _config.dpis.value().begin(); - - run_task([self_weak = self(), dpi = *_current_dpi] { - if (auto self = self_weak.lock()) { - try { - self->_dpi->setDPI(dpi, self->_config.sensor.value_or(0)); - } 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", - self->_device->hidpp20().devicePath().c_str(), - self->_device->hidpp20().deviceIndex(), dpi, - self->_config.sensor.value_or(0)); - else - throw e; - } - } - }); - } -} - -void CycleDPI::release() { - _pressed = false; -} - -uint8_t CycleDPI::reprogFlags() const { - return backend::hidpp20::ReprogControls::TemporaryDiverted; -} +/* + * Copyright 2019-2023 PixlOne + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * 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 + +using namespace logid::actions; + +const char* CycleDPI::interface_name = "CycleDPI"; + +CycleDPI::CycleDPI(Device* device, config::CycleDPI& config, + [[maybe_unused]] const std::shared_ptr& parent) : + Action(device, interface_name, { + { + {"GetDPIs", {this, &CycleDPI::getDPIs, {"dpis"}}}, + {"SetDPIs", {this, &CycleDPI::setDPIs, {"dpis"}}} + }, + {}, + {} + }), + _config(config) { + _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()); + + if (_config.dpis.has_value()) { + _current_dpi = _config.dpis.value().begin(); + } +} + +std::vector CycleDPI::getDPIs() const { + std::shared_lock lock(_config_mutex); + auto dpis = _config.dpis.value_or(std::list()); + return {dpis.begin(), dpis.end()}; +} + +void CycleDPI::setDPIs(const std::vector& dpis) { + std::unique_lock lock(_config_mutex); + std::lock_guard dpi_lock(_dpi_mutex); + _config.dpis.emplace(dpis.begin(), dpis.end()); + _current_dpi = _config.dpis->cbegin(); +} + +void CycleDPI::press() { + _pressed = true; + std::shared_lock lock(_config_mutex); + std::lock_guard dpi_lock(_dpi_mutex); + if (_dpi && _config.dpis.has_value() && _config.dpis.value().empty()) { + ++_current_dpi; + if (_current_dpi == _config.dpis.value().end()) + _current_dpi = _config.dpis.value().begin(); + + run_task([self_weak = self(), dpi = *_current_dpi] { + if (auto self = self_weak.lock()) { + try { + self->_dpi->setDPI(dpi, self->_config.sensor.value_or(0)); + } 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", + self->_device->hidpp20().devicePath().c_str(), + self->_device->hidpp20().deviceIndex(), dpi, + self->_config.sensor.value_or(0)); + else + throw e; + } + } + }); + } +} + +void CycleDPI::release() { + _pressed = false; +} + +uint8_t CycleDPI::reprogFlags() const { + return backend::hidpp20::ReprogControls::TemporaryDiverted; +} diff --git a/src/logid/actions/CycleDPI.h b/src/logid/actions/CycleDPI.h index 828f513..e069108 100644 --- a/src/logid/actions/CycleDPI.h +++ b/src/logid/actions/CycleDPI.h @@ -1,50 +1,50 @@ -/* - * Copyright 2019-2023 PixlOne - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * 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 - -namespace logid::actions { - class CycleDPI : public Action { - public: - static const char* interface_name; - - CycleDPI(Device* device, config::CycleDPI& setting, - const std::shared_ptr& parent); - - void press() final; - - void release() final; - - [[nodiscard]] std::vector getDPIs() const; - - void setDPIs(const std::vector& dpis); - - [[nodiscard]] uint8_t reprogFlags() const final; - - protected: - std::mutex _dpi_mutex; - config::CycleDPI& _config; - std::shared_ptr _dpi; - std::list::const_iterator _current_dpi; - }; -} - +/* + * Copyright 2019-2023 PixlOne + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * 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 + +namespace logid::actions { + class CycleDPI : public Action { + public: + static const char* interface_name; + + CycleDPI(Device* device, config::CycleDPI& setting, + const std::shared_ptr& parent); + + void press() final; + + void release() final; + + [[nodiscard]] std::vector getDPIs() const; + + void setDPIs(const std::vector& dpis); + + [[nodiscard]] uint8_t reprogFlags() const final; + + protected: + std::mutex _dpi_mutex; + config::CycleDPI& _config; + std::shared_ptr _dpi; + std::list::const_iterator _current_dpi; + }; +} + #endif //LOGID_ACTION_CYCLEDPI_H \ No newline at end of file diff --git a/src/logid/actions/GestureAction.cpp b/src/logid/actions/GestureAction.cpp index 8afceb6..09484b9 100644 --- a/src/logid/actions/GestureAction.cpp +++ b/src/logid/actions/GestureAction.cpp @@ -1,261 +1,261 @@ -/* - * Copyright 2019-2023 PixlOne - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * 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 - -using namespace logid::actions; -using namespace logid; -using namespace logid::backend; - -const char* GestureAction::interface_name = "Gesture"; - -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"); -} - -std::string GestureAction::fromDirection(Direction direction) { - switch (direction) { - case Up: - return "up"; - case Down: - return "down"; - case Left: - return "left"; - case Right: - return "right"; - case None: - return "none"; - } - - // This shouldn't happen - throw InvalidGesture(); -} - -GestureAction::Direction GestureAction::toDirection(int32_t x, int32_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, config::GestureAction& config, - const std::shared_ptr& parent) : - Action(dev, interface_name, - { - { - {"SetGesture", {this, &GestureAction::setGesture, {"direction", "type"}}} - }, - {}, - {} - }), - _node(parent->make_child("gestures")), _config(config) { - if (_config.gestures.has_value()) { - auto& gestures = _config.gestures.value(); - for (auto&& x: gestures) { - try { - auto direction = toDirection(x.first); - _gestures.emplace( - direction,Gesture::makeGesture( - dev, x.second, - _node->make_child(fromDirection(direction)))); - if (direction == None) { - auto& gesture = x.second; - std::visit([](auto&& x) { - x.threshold.emplace(0); - }, gesture); - } - } catch (std::invalid_argument& e) { - logPrintf(WARN, "%s is not a direction", x.first.c_str()); - } - } - } -} - -void GestureAction::press() { - std::shared_lock lock(_config_mutex); - - _pressed = true; - _x = 0, _y = 0; - for (auto& gesture: _gestures) - gesture.second->press(false); -} - -void GestureAction::release() { - std::shared_lock lock(_config_mutex); - - _pressed = false; - bool threshold_met = false; - - auto d = toDirection(_x, _y); - auto primary_gesture = _gestures.find(d); - if (primary_gesture != _gestures.end()) { - threshold_met = primary_gesture->second->metThreshold(); - primary_gesture->second->release(true); - } - - for (auto& gesture: _gestures) { - if (gesture.first == d || gesture.first == None) - 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); - } - } else { - gesture.second->release(false); - } - } - - auto none_gesture = _gestures.find(None); - if (none_gesture != _gestures.end()) { - none_gesture->second->release(!threshold_met); - } -} - -void GestureAction::move(int16_t x, int16_t y) { - std::shared_lock lock(_config_mutex); - - int32_t new_x = _x + x, new_y = _y + y; - - if (abs(x) > 0) { - if (_x < 0 && new_x >= 0) { // Left -> Origin/Right - auto left = _gestures.find(Left); - if (left != _gestures.end() && left->second) - left->second->move((int16_t) _x); - if (new_x) { // Ignore to origin - auto right = _gestures.find(Right); - if (right != _gestures.end() && right->second) - right->second->move((int16_t) new_x); - } - } else if (_x > 0 && new_x <= 0) { // Right -> Origin/Left - auto right = _gestures.find(Right); - if (right != _gestures.end() && right->second) - right->second->move((int16_t) -_x); - if (new_x) { // Ignore to origin - auto left = _gestures.find(Left); - if (left != _gestures.end() && left->second) - left->second->move((int16_t) -new_x); - } - } else if (new_x < 0) { // Origin/Left to Left - auto left = _gestures.find(Left); - if (left != _gestures.end() && left->second) - left->second->move((int16_t) -x); - } else if (new_x > 0) { // Origin/Right to Right - auto right = _gestures.find(Right); - if (right != _gestures.end() && right->second) - right->second->move(x); - } - } - - if (abs(y) > 0) { - if (_y > 0 && new_y <= 0) { // Up -> Origin/Down - auto up = _gestures.find(Up); - if (up != _gestures.end() && up->second) - up->second->move((int16_t) _y); - if (new_y) { // Ignore to origin - auto down = _gestures.find(Down); - if (down != _gestures.end() && down->second) - down->second->move((int16_t) new_y); - } - } else if (_y < 0 && new_y >= 0) { // Down -> Origin/Up - auto down = _gestures.find(Down); - if (down != _gestures.end() && down->second) - down->second->move((int16_t) -_y); - if (new_y) { // Ignore to origin - auto up = _gestures.find(Up); - if (up != _gestures.end() && up->second) - up->second->move((int16_t) -new_y); - } - } else if (new_y < 0) { // Origin/Up to Up - auto up = _gestures.find(Up); - if (up != _gestures.end() && up->second) - up->second->move((int16_t) -y); - } else if (new_y > 0) {// Origin/Down to Down - auto down = _gestures.find(Down); - if (down != _gestures.end() && down->second) - down->second->move(y); - } - } - - _x = new_x; - _y = new_y; -} - -uint8_t GestureAction::reprogFlags() const { - return (hidpp20::ReprogControls::TemporaryDiverted | - hidpp20::ReprogControls::RawXYDiverted); -} - -void GestureAction::setGesture(const std::string& direction, const std::string& type) { - std::unique_lock lock(_config_mutex); - - Direction d = toDirection(direction); - - auto it = _gestures.find(d); - - if (it != _gestures.end()) { - if (pressed()) { - auto current = toDirection(_x, _y); - if (it->second) - it->second->release(current == d); - } - } - - auto dir_name = fromDirection(d); - - auto& gesture = _config.gestures.value()[dir_name]; - - _gestures[d].reset(); - try { - _gestures[d] = Gesture::makeGesture( - _device, type, gesture, - _node->make_child(dir_name)); - } catch (InvalidGesture& e) { - _gestures[d] = Gesture::makeGesture( - _device, gesture, - _node->make_child(dir_name)); - throw std::invalid_argument("Invalid gesture type"); - } - - if (d == None) { - std::visit([](auto&& x) { - x.threshold = 0; - }, gesture); - } -} +/* + * Copyright 2019-2023 PixlOne + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * 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 + +using namespace logid::actions; +using namespace logid; +using namespace logid::backend; + +const char* GestureAction::interface_name = "Gesture"; + +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"); +} + +std::string GestureAction::fromDirection(Direction direction) { + switch (direction) { + case Up: + return "up"; + case Down: + return "down"; + case Left: + return "left"; + case Right: + return "right"; + case None: + return "none"; + } + + // This shouldn't happen + throw InvalidGesture(); +} + +GestureAction::Direction GestureAction::toDirection(int32_t x, int32_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, config::GestureAction& config, + const std::shared_ptr& parent) : + Action(dev, interface_name, + { + { + {"SetGesture", {this, &GestureAction::setGesture, {"direction", "type"}}} + }, + {}, + {} + }), + _node(parent->make_child("gestures")), _config(config) { + if (_config.gestures.has_value()) { + auto& gestures = _config.gestures.value(); + for (auto&& x: gestures) { + try { + auto direction = toDirection(x.first); + _gestures.emplace( + direction,Gesture::makeGesture( + dev, x.second, + _node->make_child(fromDirection(direction)))); + if (direction == None) { + auto& gesture = x.second; + std::visit([](auto&& x) { + x.threshold.emplace(0); + }, gesture); + } + } catch (std::invalid_argument& e) { + logPrintf(WARN, "%s is not a direction", x.first.c_str()); + } + } + } +} + +void GestureAction::press() { + std::shared_lock lock(_config_mutex); + + _pressed = true; + _x = 0, _y = 0; + for (auto& gesture: _gestures) + gesture.second->press(false); +} + +void GestureAction::release() { + std::shared_lock lock(_config_mutex); + + _pressed = false; + bool threshold_met = false; + + auto d = toDirection(_x, _y); + auto primary_gesture = _gestures.find(d); + if (primary_gesture != _gestures.end()) { + threshold_met = primary_gesture->second->metThreshold(); + primary_gesture->second->release(true); + } + + for (auto& gesture: _gestures) { + if (gesture.first == d || gesture.first == None) + 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); + } + } else { + gesture.second->release(false); + } + } + + auto none_gesture = _gestures.find(None); + if (none_gesture != _gestures.end()) { + none_gesture->second->release(!threshold_met); + } +} + +void GestureAction::move(int16_t x, int16_t y) { + std::shared_lock lock(_config_mutex); + + int32_t new_x = _x + x, new_y = _y + y; + + if (abs(x) > 0) { + if (_x < 0 && new_x >= 0) { // Left -> Origin/Right + auto left = _gestures.find(Left); + if (left != _gestures.end() && left->second) + left->second->move((int16_t) _x); + if (new_x) { // Ignore to origin + auto right = _gestures.find(Right); + if (right != _gestures.end() && right->second) + right->second->move((int16_t) new_x); + } + } else if (_x > 0 && new_x <= 0) { // Right -> Origin/Left + auto right = _gestures.find(Right); + if (right != _gestures.end() && right->second) + right->second->move((int16_t) -_x); + if (new_x) { // Ignore to origin + auto left = _gestures.find(Left); + if (left != _gestures.end() && left->second) + left->second->move((int16_t) -new_x); + } + } else if (new_x < 0) { // Origin/Left to Left + auto left = _gestures.find(Left); + if (left != _gestures.end() && left->second) + left->second->move((int16_t) -x); + } else if (new_x > 0) { // Origin/Right to Right + auto right = _gestures.find(Right); + if (right != _gestures.end() && right->second) + right->second->move(x); + } + } + + if (abs(y) > 0) { + if (_y > 0 && new_y <= 0) { // Up -> Origin/Down + auto up = _gestures.find(Up); + if (up != _gestures.end() && up->second) + up->second->move((int16_t) _y); + if (new_y) { // Ignore to origin + auto down = _gestures.find(Down); + if (down != _gestures.end() && down->second) + down->second->move((int16_t) new_y); + } + } else if (_y < 0 && new_y >= 0) { // Down -> Origin/Up + auto down = _gestures.find(Down); + if (down != _gestures.end() && down->second) + down->second->move((int16_t) -_y); + if (new_y) { // Ignore to origin + auto up = _gestures.find(Up); + if (up != _gestures.end() && up->second) + up->second->move((int16_t) -new_y); + } + } else if (new_y < 0) { // Origin/Up to Up + auto up = _gestures.find(Up); + if (up != _gestures.end() && up->second) + up->second->move((int16_t) -y); + } else if (new_y > 0) {// Origin/Down to Down + auto down = _gestures.find(Down); + if (down != _gestures.end() && down->second) + down->second->move(y); + } + } + + _x = new_x; + _y = new_y; +} + +uint8_t GestureAction::reprogFlags() const { + return (hidpp20::ReprogControls::TemporaryDiverted | + hidpp20::ReprogControls::RawXYDiverted); +} + +void GestureAction::setGesture(const std::string& direction, const std::string& type) { + std::unique_lock lock(_config_mutex); + + Direction d = toDirection(direction); + + auto it = _gestures.find(d); + + if (it != _gestures.end()) { + if (pressed()) { + auto current = toDirection(_x, _y); + if (it->second) + it->second->release(current == d); + } + } + + auto dir_name = fromDirection(d); + + auto& gesture = _config.gestures.value()[dir_name]; + + _gestures[d].reset(); + try { + _gestures[d] = Gesture::makeGesture( + _device, type, gesture, + _node->make_child(dir_name)); + } catch (InvalidGesture& e) { + _gestures[d] = Gesture::makeGesture( + _device, gesture, + _node->make_child(dir_name)); + throw std::invalid_argument("Invalid gesture type"); + } + + if (d == None) { + std::visit([](auto&& x) { + x.threshold = 0; + }, gesture); + } +} diff --git a/src/logid/actions/GestureAction.h b/src/logid/actions/GestureAction.h index 7ef8f3a..d86d0ed 100644 --- a/src/logid/actions/GestureAction.h +++ b/src/logid/actions/GestureAction.h @@ -1,66 +1,66 @@ -/* - * Copyright 2019-2023 PixlOne - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * 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 - -namespace logid::actions { - class GestureAction : public Action { - public: - static const char* interface_name; - - enum Direction { - None, - Up, - Down, - Left, - Right - }; - - static Direction toDirection(std::string direction); - - static std::string fromDirection(Direction direction); - - static Direction toDirection(int32_t x, int32_t y); - - GestureAction(Device* dev, config::GestureAction& config, - const std::shared_ptr& parent); - - void press() final; - - void release() final; - - void move(int16_t x, int16_t y) final; - - uint8_t reprogFlags() const final; - - void setGesture(const std::string& direction, - const std::string& type); - - protected: - int32_t _x{}, _y{}; - std::shared_ptr _node; - std::map> _gestures; - config::GestureAction& _config; - }; -} - -#endif //LOGID_ACTION_GESTUREACTION_H +/* + * Copyright 2019-2023 PixlOne + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * 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 + +namespace logid::actions { + class GestureAction : public Action { + public: + static const char* interface_name; + + enum Direction { + None, + Up, + Down, + Left, + Right + }; + + static Direction toDirection(std::string direction); + + static std::string fromDirection(Direction direction); + + static Direction toDirection(int32_t x, int32_t y); + + GestureAction(Device* dev, config::GestureAction& config, + const std::shared_ptr& parent); + + void press() final; + + void release() final; + + void move(int16_t x, int16_t y) final; + + uint8_t reprogFlags() const final; + + void setGesture(const std::string& direction, + const std::string& type); + + protected: + int32_t _x{}, _y{}; + std::shared_ptr _node; + std::map> _gestures; + config::GestureAction& _config; + }; +} + +#endif //LOGID_ACTION_GESTUREACTION_H diff --git a/src/logid/actions/KeypressAction.cpp b/src/logid/actions/KeypressAction.cpp index ee8d1de..13f7808 100644 --- a/src/logid/actions/KeypressAction.cpp +++ b/src/logid/actions/KeypressAction.cpp @@ -1,126 +1,126 @@ -/* - * Copyright 2019-2023 PixlOne - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * 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 - -using namespace logid::actions; -using namespace logid::backend; - -const char* KeypressAction::interface_name = "Keypress"; - -KeypressAction::KeypressAction( - Device* device, config::KeypressAction& config, - [[maybe_unused]] const std::shared_ptr& parent) : - Action(device, interface_name, { - { - {"GetKeys", {this, &KeypressAction::getKeys, {"keys"}}}, - {"SetKeys", {this, &KeypressAction::setKeys, {"keys"}}} - }, - {}, - {} - }), _config(config) { - _setConfig(); -} - -void KeypressAction::press() { - std::shared_lock lock(_config_mutex); - _pressed = true; - for (auto& key: _keys) - _device->virtualInput()->pressKey(key); -} - -void KeypressAction::release() { - std::shared_lock lock(_config_mutex); - _pressed = false; - for (auto& key: _keys) - _device->virtualInput()->releaseKey(key); -} - -void KeypressAction::_setConfig() { - _keys.clear(); - - if (!_config.keys.has_value()) - return; - - auto& config = _config.keys.value(); - - if (std::holds_alternative(config)) { - const auto& key = std::get(config); - try { - auto code = _device->virtualInput()->toKeyCode(key); - _device->virtualInput()->registerKey(code); - _keys.emplace_back(code); - } catch (InputDevice::InvalidEventCode& e) { - logPrintf(WARN, "Invalid keycode %s, skipping.", key.c_str()); - } - } else if (std::holds_alternative(_config.keys.value())) { - const auto& key = std::get(config); - _device->virtualInput()->registerKey(key); - _keys.emplace_back(key); - } else if (std::holds_alternative< - std::list>>(config)) { - const auto& keys = std::get< - std::list>>(config); - for (const auto& key: keys) { - if (std::holds_alternative(key)) { - const auto& key_str = std::get(key); - try { - auto code = _device->virtualInput()->toKeyCode(key_str); - _device->virtualInput()->registerKey(code); - _keys.emplace_back(code); - } catch (InputDevice::InvalidEventCode& e) { - logPrintf(WARN, "Invalid keycode %s, skipping.", - key_str.c_str()); - } - } else if (std::holds_alternative(key)) { - auto& code = std::get(key); - _device->virtualInput()->registerKey(code); - _keys.emplace_back(code); - } - } - } -} - -uint8_t KeypressAction::reprogFlags() const { - return hidpp20::ReprogControls::TemporaryDiverted; -} - -std::vector KeypressAction::getKeys() const { - std::shared_lock lock(_config_mutex); - std::vector ret; - for (auto& x: _keys) - ret.push_back(InputDevice::toKeyName(x)); - - return ret; -} - -void KeypressAction::setKeys(const std::vector& keys) { - std::unique_lock lock(_config_mutex); - if (_pressed) - for (auto& key: _keys) - _device->virtualInput()->releaseKey(key); - _config.keys = std::list>(); - auto& config = std::get>>( - _config.keys.value()); - for (auto& x: keys) - config.emplace_back(x); - _setConfig(); -} +/* + * Copyright 2019-2023 PixlOne + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * 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 + +using namespace logid::actions; +using namespace logid::backend; + +const char* KeypressAction::interface_name = "Keypress"; + +KeypressAction::KeypressAction( + Device* device, config::KeypressAction& config, + [[maybe_unused]] const std::shared_ptr& parent) : + Action(device, interface_name, { + { + {"GetKeys", {this, &KeypressAction::getKeys, {"keys"}}}, + {"SetKeys", {this, &KeypressAction::setKeys, {"keys"}}} + }, + {}, + {} + }), _config(config) { + _setConfig(); +} + +void KeypressAction::press() { + std::shared_lock lock(_config_mutex); + _pressed = true; + for (auto& key: _keys) + _device->virtualInput()->pressKey(key); +} + +void KeypressAction::release() { + std::shared_lock lock(_config_mutex); + _pressed = false; + for (auto& key: _keys) + _device->virtualInput()->releaseKey(key); +} + +void KeypressAction::_setConfig() { + _keys.clear(); + + if (!_config.keys.has_value()) + return; + + auto& config = _config.keys.value(); + + if (std::holds_alternative(config)) { + const auto& key = std::get(config); + try { + auto code = _device->virtualInput()->toKeyCode(key); + _device->virtualInput()->registerKey(code); + _keys.emplace_back(code); + } catch (InputDevice::InvalidEventCode& e) { + logPrintf(WARN, "Invalid keycode %s, skipping.", key.c_str()); + } + } else if (std::holds_alternative(_config.keys.value())) { + const auto& key = std::get(config); + _device->virtualInput()->registerKey(key); + _keys.emplace_back(key); + } else if (std::holds_alternative< + std::list>>(config)) { + const auto& keys = std::get< + std::list>>(config); + for (const auto& key: keys) { + if (std::holds_alternative(key)) { + const auto& key_str = std::get(key); + try { + auto code = _device->virtualInput()->toKeyCode(key_str); + _device->virtualInput()->registerKey(code); + _keys.emplace_back(code); + } catch (InputDevice::InvalidEventCode& e) { + logPrintf(WARN, "Invalid keycode %s, skipping.", + key_str.c_str()); + } + } else if (std::holds_alternative(key)) { + auto& code = std::get(key); + _device->virtualInput()->registerKey(code); + _keys.emplace_back(code); + } + } + } +} + +uint8_t KeypressAction::reprogFlags() const { + return hidpp20::ReprogControls::TemporaryDiverted; +} + +std::vector KeypressAction::getKeys() const { + std::shared_lock lock(_config_mutex); + std::vector ret; + for (auto& x: _keys) + ret.push_back(InputDevice::toKeyName(x)); + + return ret; +} + +void KeypressAction::setKeys(const std::vector& keys) { + std::unique_lock lock(_config_mutex); + if (_pressed) + for (auto& key: _keys) + _device->virtualInput()->releaseKey(key); + _config.keys = std::list>(); + auto& config = std::get>>( + _config.keys.value()); + for (auto& x: keys) + config.emplace_back(x); + _setConfig(); +} diff --git a/src/logid/actions/KeypressAction.h b/src/logid/actions/KeypressAction.h index 226db94..a7e1816 100644 --- a/src/logid/actions/KeypressAction.h +++ b/src/logid/actions/KeypressAction.h @@ -1,51 +1,51 @@ -/* - * Copyright 2019-2023 PixlOne - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * 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_KEYPRESS_H -#define LOGID_ACTION_KEYPRESS_H - -#include -#include - -namespace logid::actions { - class KeypressAction : public Action { - public: - static const char* interface_name; - - KeypressAction(Device* dev, - config::KeypressAction& config, - const std::shared_ptr& parent); - - void press() final; - - void release() final; - - [[nodiscard]] std::vector getKeys() const; - - void setKeys(const std::vector& keys); - - [[nodiscard]] uint8_t reprogFlags() const final; - - protected: - config::KeypressAction& _config; - std::list _keys; - - void _setConfig(); - }; -} - -#endif //LOGID_ACTION_KEYPRESS_H +/* + * Copyright 2019-2023 PixlOne + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * 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_KEYPRESS_H +#define LOGID_ACTION_KEYPRESS_H + +#include +#include + +namespace logid::actions { + class KeypressAction : public Action { + public: + static const char* interface_name; + + KeypressAction(Device* dev, + config::KeypressAction& config, + const std::shared_ptr& parent); + + void press() final; + + void release() final; + + [[nodiscard]] std::vector getKeys() const; + + void setKeys(const std::vector& keys); + + [[nodiscard]] uint8_t reprogFlags() const final; + + protected: + config::KeypressAction& _config; + std::list _keys; + + void _setConfig(); + }; +} + +#endif //LOGID_ACTION_KEYPRESS_H diff --git a/src/logid/actions/NullAction.cpp b/src/logid/actions/NullAction.cpp index 0734aa6..e613c0f 100644 --- a/src/logid/actions/NullAction.cpp +++ b/src/logid/actions/NullAction.cpp @@ -1,41 +1,41 @@ -/* - * Copyright 2019-2023 PixlOne - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * 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 - -using namespace logid::actions; - -const char* NullAction::interface_name = "None"; - -NullAction::NullAction( - Device* device, - [[maybe_unused]] const std::shared_ptr& parent) : - Action(device, interface_name) { -} - -void NullAction::press() { - _pressed = true; -} - -void NullAction::release() { - _pressed = false; -} - -uint8_t NullAction::reprogFlags() const { - return backend::hidpp20::ReprogControls::TemporaryDiverted; +/* + * Copyright 2019-2023 PixlOne + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * 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 + +using namespace logid::actions; + +const char* NullAction::interface_name = "None"; + +NullAction::NullAction( + Device* device, + [[maybe_unused]] const std::shared_ptr& parent) : + Action(device, interface_name) { +} + +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 index 1aeca57..3bce2fc 100644 --- a/src/logid/actions/NullAction.h +++ b/src/logid/actions/NullAction.h @@ -1,44 +1,44 @@ -/* - * Copyright 2019-2023 PixlOne - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * 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 - -namespace logid::actions { - class NullAction : public Action { - public: - static const char* interface_name; - - NullAction(Device* device, - const std::shared_ptr& parent); - - NullAction(Device* device, [[maybe_unused]] config::NoAction& config, - const std::shared_ptr& parent) : - NullAction(device, parent) {} - - void press() final; - - void release() final; - - [[nodiscard]] uint8_t reprogFlags() const final; - }; -} - - -#endif //LOGID_ACTION_NULL_H +/* + * Copyright 2019-2023 PixlOne + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * 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 + +namespace logid::actions { + class NullAction : public Action { + public: + static const char* interface_name; + + NullAction(Device* device, + const std::shared_ptr& parent); + + NullAction(Device* device, [[maybe_unused]] config::NoAction& config, + const std::shared_ptr& parent) : + NullAction(device, parent) {} + + void press() final; + + void release() final; + + [[nodiscard]] uint8_t reprogFlags() const final; + }; +} + + +#endif //LOGID_ACTION_NULL_H diff --git a/src/logid/actions/ToggleHiresScroll.cpp b/src/logid/actions/ToggleHiresScroll.cpp index cf8eb4d..f868519 100644 --- a/src/logid/actions/ToggleHiresScroll.cpp +++ b/src/logid/actions/ToggleHiresScroll.cpp @@ -1,60 +1,60 @@ -/* - * Copyright 2019-2023 PixlOne - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * 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 - -using namespace logid::actions; -using namespace logid::backend; - -const char* ToggleHiresScroll::interface_name = "ToggleHiresScroll"; - -ToggleHiresScroll::ToggleHiresScroll( - Device* dev, - [[maybe_unused]] const std::shared_ptr& parent) : - Action(dev, interface_name) { - _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) { - run_task([self_weak = self()]() { - if (auto self = self_weak.lock()) { - auto mode = self->_hires_scroll->getMode(); - mode ^= backend::hidpp20::HiresScroll::HiRes; - self->_hires_scroll->setMode(mode); - } - }); - } -} - -void ToggleHiresScroll::release() { - _pressed = false; -} - -uint8_t ToggleHiresScroll::reprogFlags() const { - return hidpp20::ReprogControls::TemporaryDiverted; -} +/* + * Copyright 2019-2023 PixlOne + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * 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 + +using namespace logid::actions; +using namespace logid::backend; + +const char* ToggleHiresScroll::interface_name = "ToggleHiresScroll"; + +ToggleHiresScroll::ToggleHiresScroll( + Device* dev, + [[maybe_unused]] const std::shared_ptr& parent) : + Action(dev, interface_name) { + _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) { + run_task([self_weak = self()]() { + if (auto self = self_weak.lock()) { + auto mode = self->_hires_scroll->getMode(); + mode ^= backend::hidpp20::HiresScroll::HiRes; + self->_hires_scroll->setMode(mode); + } + }); + } +} + +void ToggleHiresScroll::release() { + _pressed = false; +} + +uint8_t ToggleHiresScroll::reprogFlags() const { + return hidpp20::ReprogControls::TemporaryDiverted; +} diff --git a/src/logid/actions/ToggleHiresScroll.h b/src/logid/actions/ToggleHiresScroll.h index 3376c4f..acf9c9b 100644 --- a/src/logid/actions/ToggleHiresScroll.h +++ b/src/logid/actions/ToggleHiresScroll.h @@ -1,47 +1,47 @@ -/* - * Copyright 2019-2023 PixlOne - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * 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 -#include - -namespace logid::actions { - class ToggleHiresScroll : public Action { - public: - static const char* interface_name; - - ToggleHiresScroll(Device* dev, const std::shared_ptr& parent); - - ToggleHiresScroll(Device* device, - [[maybe_unused]] config::ToggleHiresScroll& action, - const std::shared_ptr& parent) : - ToggleHiresScroll(device, parent) {} - - void press() final; - - void release() final; - - [[nodiscard]] uint8_t reprogFlags() const final; - - protected: - std::shared_ptr _hires_scroll; - }; -} - -#endif //LOGID_ACTION_TOGGLEHIRESSCROLL_H +/* + * Copyright 2019-2023 PixlOne + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * 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 +#include + +namespace logid::actions { + class ToggleHiresScroll : public Action { + public: + static const char* interface_name; + + ToggleHiresScroll(Device* dev, const std::shared_ptr& parent); + + ToggleHiresScroll(Device* device, + [[maybe_unused]] config::ToggleHiresScroll& action, + const std::shared_ptr& parent) : + ToggleHiresScroll(device, parent) {} + + void press() final; + + void release() final; + + [[nodiscard]] uint8_t reprogFlags() const final; + + protected: + std::shared_ptr _hires_scroll; + }; +} + +#endif //LOGID_ACTION_TOGGLEHIRESSCROLL_H diff --git a/src/logid/actions/ToggleSmartShift.cpp b/src/logid/actions/ToggleSmartShift.cpp index 598cbff..853e133 100644 --- a/src/logid/actions/ToggleSmartShift.cpp +++ b/src/logid/actions/ToggleSmartShift.cpp @@ -1,62 +1,62 @@ -/* - * Copyright 2019-2023 PixlOne - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * 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 - -using namespace logid::actions; -using namespace logid::backend; - -const char* ToggleSmartShift::interface_name = "ToggleSmartShift"; - -ToggleSmartShift::ToggleSmartShift( - Device* dev, - [[maybe_unused]] const std::shared_ptr& parent) : - Action(dev, interface_name) { - _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().deviceIndex()); -} - -void ToggleSmartShift::press() { - _pressed = true; - if (_smartshift) { - run_task([self_weak = self()]() { - if (auto self = self_weak.lock()) { - auto status = self->_smartshift->getStatus(); - status.setActive = true; - status.active = !status.active; - self->_smartshift->setStatus(status); - } - }); - } -} - -void ToggleSmartShift::release() { - _pressed = false; -} - -uint8_t ToggleSmartShift::reprogFlags() const { - return hidpp20::ReprogControls::TemporaryDiverted; -} +/* + * Copyright 2019-2023 PixlOne + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * 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 + +using namespace logid::actions; +using namespace logid::backend; + +const char* ToggleSmartShift::interface_name = "ToggleSmartShift"; + +ToggleSmartShift::ToggleSmartShift( + Device* dev, + [[maybe_unused]] const std::shared_ptr& parent) : + Action(dev, interface_name) { + _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().deviceIndex()); +} + +void ToggleSmartShift::press() { + _pressed = true; + if (_smartshift) { + run_task([self_weak = self()]() { + if (auto self = self_weak.lock()) { + auto status = self->_smartshift->getStatus(); + status.setActive = true; + status.active = !status.active; + self->_smartshift->setStatus(status); + } + }); + } +} + +void ToggleSmartShift::release() { + _pressed = false; +} + +uint8_t ToggleSmartShift::reprogFlags() const { + return hidpp20::ReprogControls::TemporaryDiverted; +} diff --git a/src/logid/actions/ToggleSmartShift.h b/src/logid/actions/ToggleSmartShift.h index e987ccd..0ddab92 100644 --- a/src/logid/actions/ToggleSmartShift.h +++ b/src/logid/actions/ToggleSmartShift.h @@ -1,48 +1,48 @@ -/* - * Copyright 2019-2023 PixlOne - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * 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 - -namespace logid::actions { - class ToggleSmartShift : public Action { - public: - static const char* interface_name; - - ToggleSmartShift(Device* dev, - const std::shared_ptr& parent); - - ToggleSmartShift(Device* device, - [[maybe_unused]] config::ToggleSmartShift& action, - const std::shared_ptr& parent) : - ToggleSmartShift(device, parent) {} - - void press() final; - - void release() final; - - [[nodiscard]] uint8_t reprogFlags() const final; - - protected: - std::shared_ptr _smartshift; - }; -} - +/* + * Copyright 2019-2023 PixlOne + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * 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 + +namespace logid::actions { + class ToggleSmartShift : public Action { + public: + static const char* interface_name; + + ToggleSmartShift(Device* dev, + const std::shared_ptr& parent); + + ToggleSmartShift(Device* device, + [[maybe_unused]] config::ToggleSmartShift& action, + const std::shared_ptr& parent) : + ToggleSmartShift(device, parent) {} + + void press() final; + + void release() final; + + [[nodiscard]] uint8_t reprogFlags() const final; + + protected: + std::shared_ptr _smartshift; + }; +} + #endif //LOGID_ACTION_TOGGLESMARTSHIFT_H \ No newline at end of file diff --git a/src/logid/actions/gesture/AxisGesture.cpp b/src/logid/actions/gesture/AxisGesture.cpp index ac4a810..9e5d4b7 100644 --- a/src/logid/actions/gesture/AxisGesture.cpp +++ b/src/logid/actions/gesture/AxisGesture.cpp @@ -1,185 +1,185 @@ -/* - * Copyright 2019-2023 PixlOne - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * 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 - -using namespace logid::actions; - -const char* AxisGesture::interface_name = "Axis"; - -AxisGesture::AxisGesture(Device* device, config::AxisGesture& config, - const std::shared_ptr& parent) : - Gesture(device, parent, interface_name, { - { - {"GetConfig", {this, &AxisGesture::getConfig, {"axis", "multiplier", "threshold"}}}, - {"SetThreshold", {this, &AxisGesture::setThreshold, {"threshold"}}}, - {"SetMultiplier", {this, &AxisGesture::setMultiplier, {"multiplier"}}}, - {"SetAxis", {this, &AxisGesture::setAxis, {"axis"}}} - }, - {}, - {} - }), _multiplier(1), _config(config) { - if (_config.axis.has_value()) { - if (std::holds_alternative(_config.axis.value())) { - _input_axis = std::get(_config.axis.value()); - } else { - const auto& axis = std::get(_config.axis.value()); - try { - _input_axis = _device->virtualInput()->toAxisCode(axis); - } catch (InputDevice::InvalidEventCode& e) { - logPrintf(WARN, "Invalid axis %s."); - } - } - - } - - if (_input_axis.has_value()) - _device->virtualInput()->registerAxis(_input_axis.value()); -} - -void AxisGesture::press(bool init_threshold) { - std::shared_lock lock(_config_mutex); - if (init_threshold) { - _axis = (int32_t) (_config.threshold.value_or(defaults::gesture_threshold)); - } else { - _axis = 0; - } - _axis_remainder = 0; - _hires_remainder = 0; -} - -void AxisGesture::release(bool primary) { - // Do nothing - (void) primary; // Suppress unused warning -} - -void AxisGesture::move(int16_t axis) { - std::shared_lock lock(_config_mutex); - if (!_input_axis.has_value()) - return; - - const auto threshold = _config.threshold.value_or( - defaults::gesture_threshold); - int32_t new_axis = _axis + axis; - int low_res_axis = InputDevice::getLowResAxis(axis); - int hires_remainder = _hires_remainder; - - if (new_axis > threshold) { - double move = axis; - if (_axis < threshold) - move = new_axis - threshold; - bool negative_multiplier = _config.axis_multiplier.value_or(1) < 0; - if (negative_multiplier) - move *= -_config.axis_multiplier.value_or(1); - else - move *= _config.axis_multiplier.value_or(1); - // Handle hi-res multiplier - move *= _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; - - if (low_res_axis != -1) { - int lowres_movement, hires_movement = (int) move_floor; - _device->virtualInput()->moveAxis(_input_axis.value(), hires_movement); - hires_remainder += hires_movement; - if (abs(hires_remainder) >= 60) { - lowres_movement = hires_remainder / 120; - if (lowres_movement == 0) - lowres_movement = hires_remainder > 0 ? 1 : -1; - hires_remainder -= lowres_movement * 120; - _device->virtualInput()->moveAxis(low_res_axis, lowres_movement); - } - - _hires_remainder = hires_remainder; - } else { - _device->virtualInput()->moveAxis(_input_axis.value(), (int) move_floor); - } - } - _axis = new_axis; -} - -bool AxisGesture::metThreshold() const { - std::shared_lock lock(_config_mutex); - return _axis >= _config.threshold.value_or(defaults::gesture_threshold); -} - -bool AxisGesture::wheelCompatibility() const { - return true; -} - -void AxisGesture::setHiresMultiplier(double multiplier) { - _hires_multiplier = multiplier; - if (_input_axis.has_value()) { - if (InputDevice::getLowResAxis(_input_axis.value()) != -1) - _multiplier = _config.axis_multiplier.value_or(1) * multiplier; - } -} - -std::tuple AxisGesture::getConfig() const { - std::shared_lock lock(_config_mutex); - std::string axis; - if (_config.axis.has_value()) { - if (std::holds_alternative(_config.axis.value())) { - axis = std::get(_config.axis.value()); - } else { - axis = _device->virtualInput()->toAxisName(std::get(_config.axis.value())); - } - } - - return {axis, _config.axis_multiplier.value_or(1), _config.threshold.value_or(0)}; -} - -void AxisGesture::setAxis(const std::string& axis) { - std::unique_lock lock(_config_mutex); - if (axis.empty()) { - _config.axis.reset(); - _input_axis.reset(); - } else { - _input_axis = _device->virtualInput()->toAxisCode(axis); - _config.axis = axis; - _device->virtualInput()->registerAxis(_input_axis.value()); - } - setHiresMultiplier(_hires_multiplier); -} - -void AxisGesture::setMultiplier(double multiplier) { - std::unique_lock lock(_config_mutex); - _config.axis_multiplier = multiplier; - _multiplier = multiplier; - setHiresMultiplier(_hires_multiplier); -} - -void AxisGesture::setThreshold(int threshold) { - std::unique_lock lock(_config_mutex); - if (threshold == 0) - _config.threshold.reset(); - else - _config.threshold = threshold; -} +/* + * Copyright 2019-2023 PixlOne + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * 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 + +using namespace logid::actions; + +const char* AxisGesture::interface_name = "Axis"; + +AxisGesture::AxisGesture(Device* device, config::AxisGesture& config, + const std::shared_ptr& parent) : + Gesture(device, parent, interface_name, { + { + {"GetConfig", {this, &AxisGesture::getConfig, {"axis", "multiplier", "threshold"}}}, + {"SetThreshold", {this, &AxisGesture::setThreshold, {"threshold"}}}, + {"SetMultiplier", {this, &AxisGesture::setMultiplier, {"multiplier"}}}, + {"SetAxis", {this, &AxisGesture::setAxis, {"axis"}}} + }, + {}, + {} + }), _multiplier(1), _config(config) { + if (_config.axis.has_value()) { + if (std::holds_alternative(_config.axis.value())) { + _input_axis = std::get(_config.axis.value()); + } else { + const auto& axis = std::get(_config.axis.value()); + try { + _input_axis = _device->virtualInput()->toAxisCode(axis); + } catch (InputDevice::InvalidEventCode& e) { + logPrintf(WARN, "Invalid axis %s."); + } + } + + } + + if (_input_axis.has_value()) + _device->virtualInput()->registerAxis(_input_axis.value()); +} + +void AxisGesture::press(bool init_threshold) { + std::shared_lock lock(_config_mutex); + if (init_threshold) { + _axis = (int32_t) (_config.threshold.value_or(defaults::gesture_threshold)); + } else { + _axis = 0; + } + _axis_remainder = 0; + _hires_remainder = 0; +} + +void AxisGesture::release(bool primary) { + // Do nothing + (void) primary; // Suppress unused warning +} + +void AxisGesture::move(int16_t axis) { + std::shared_lock lock(_config_mutex); + if (!_input_axis.has_value()) + return; + + const auto threshold = _config.threshold.value_or( + defaults::gesture_threshold); + int32_t new_axis = _axis + axis; + int low_res_axis = InputDevice::getLowResAxis(axis); + int hires_remainder = _hires_remainder; + + if (new_axis > threshold) { + double move = axis; + if (_axis < threshold) + move = new_axis - threshold; + bool negative_multiplier = _config.axis_multiplier.value_or(1) < 0; + if (negative_multiplier) + move *= -_config.axis_multiplier.value_or(1); + else + move *= _config.axis_multiplier.value_or(1); + // Handle hi-res multiplier + move *= _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; + + if (low_res_axis != -1) { + int lowres_movement, hires_movement = (int) move_floor; + _device->virtualInput()->moveAxis(_input_axis.value(), hires_movement); + hires_remainder += hires_movement; + if (abs(hires_remainder) >= 60) { + lowres_movement = hires_remainder / 120; + if (lowres_movement == 0) + lowres_movement = hires_remainder > 0 ? 1 : -1; + hires_remainder -= lowres_movement * 120; + _device->virtualInput()->moveAxis(low_res_axis, lowres_movement); + } + + _hires_remainder = hires_remainder; + } else { + _device->virtualInput()->moveAxis(_input_axis.value(), (int) move_floor); + } + } + _axis = new_axis; +} + +bool AxisGesture::metThreshold() const { + std::shared_lock lock(_config_mutex); + return _axis >= _config.threshold.value_or(defaults::gesture_threshold); +} + +bool AxisGesture::wheelCompatibility() const { + return true; +} + +void AxisGesture::setHiresMultiplier(double multiplier) { + _hires_multiplier = multiplier; + if (_input_axis.has_value()) { + if (InputDevice::getLowResAxis(_input_axis.value()) != -1) + _multiplier = _config.axis_multiplier.value_or(1) * multiplier; + } +} + +std::tuple AxisGesture::getConfig() const { + std::shared_lock lock(_config_mutex); + std::string axis; + if (_config.axis.has_value()) { + if (std::holds_alternative(_config.axis.value())) { + axis = std::get(_config.axis.value()); + } else { + axis = _device->virtualInput()->toAxisName(std::get(_config.axis.value())); + } + } + + return {axis, _config.axis_multiplier.value_or(1), _config.threshold.value_or(0)}; +} + +void AxisGesture::setAxis(const std::string& axis) { + std::unique_lock lock(_config_mutex); + if (axis.empty()) { + _config.axis.reset(); + _input_axis.reset(); + } else { + _input_axis = _device->virtualInput()->toAxisCode(axis); + _config.axis = axis; + _device->virtualInput()->registerAxis(_input_axis.value()); + } + setHiresMultiplier(_hires_multiplier); +} + +void AxisGesture::setMultiplier(double multiplier) { + std::unique_lock lock(_config_mutex); + _config.axis_multiplier = multiplier; + _multiplier = multiplier; + setHiresMultiplier(_hires_multiplier); +} + +void AxisGesture::setThreshold(int threshold) { + std::unique_lock lock(_config_mutex); + if (threshold == 0) + _config.threshold.reset(); + else + _config.threshold = threshold; +} diff --git a/src/logid/actions/gesture/AxisGesture.h b/src/logid/actions/gesture/AxisGesture.h index eccb5fb..a8b5e33 100644 --- a/src/logid/actions/gesture/AxisGesture.h +++ b/src/logid/actions/gesture/AxisGesture.h @@ -1,62 +1,62 @@ -/* - * Copyright 2019-2023 PixlOne - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * 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 - -namespace logid::actions { - class AxisGesture : public Gesture { - public: - static const char* interface_name; - - AxisGesture(Device* device, config::AxisGesture& config, - const std::shared_ptr& parent); - - void press(bool init_threshold) final; - - void release(bool primary) final; - - void move(int16_t axis) final; - - [[nodiscard]] bool wheelCompatibility() const final; - - [[nodiscard]] bool metThreshold() const final; - - void setHiresMultiplier(double multiplier); - - [[nodiscard]] std::tuple getConfig() const; - - void setAxis(const std::string& axis); - - void setMultiplier(double multiplier); - - void setThreshold(int threshold); - - protected: - int32_t _axis{}; - double _axis_remainder{}; - int _hires_remainder{}; - std::optional _input_axis; - double _multiplier; - double _hires_multiplier = 1.0; - config::AxisGesture& _config; - }; -} - -#endif //LOGID_ACTION_AXISGESTURE_H +/* + * Copyright 2019-2023 PixlOne + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * 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 + +namespace logid::actions { + class AxisGesture : public Gesture { + public: + static const char* interface_name; + + AxisGesture(Device* device, config::AxisGesture& config, + const std::shared_ptr& parent); + + void press(bool init_threshold) final; + + void release(bool primary) final; + + void move(int16_t axis) final; + + [[nodiscard]] bool wheelCompatibility() const final; + + [[nodiscard]] bool metThreshold() const final; + + void setHiresMultiplier(double multiplier); + + [[nodiscard]] std::tuple getConfig() const; + + void setAxis(const std::string& axis); + + void setMultiplier(double multiplier); + + void setThreshold(int threshold); + + protected: + int32_t _axis{}; + double _axis_remainder{}; + int _hires_remainder{}; + std::optional _input_axis; + double _multiplier; + double _hires_multiplier = 1.0; + config::AxisGesture& _config; + }; +} + +#endif //LOGID_ACTION_AXISGESTURE_H diff --git a/src/logid/actions/gesture/Gesture.cpp b/src/logid/actions/gesture/Gesture.cpp index 53ec4ba..8d7940e 100644 --- a/src/logid/actions/gesture/Gesture.cpp +++ b/src/logid/actions/gesture/Gesture.cpp @@ -1,88 +1,88 @@ -/* - * Copyright 2019-2023 PixlOne - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * 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 - -using namespace logid; -using namespace logid::actions; - -Gesture::Gesture(Device* device, - std::shared_ptr node, - const std::string& name, tables t) : - ipcgull::interface(SERVICE_ROOT_NAME ".Gesture." + name, std::move(t)), - _node(std::move(node)), _device(device) { -} - -namespace { - template - struct gesture_type { - typedef typename T::gesture type; - }; - - template - struct gesture_type : gesture_type { - }; - - template - struct gesture_type : gesture_type { - }; - - template - std::shared_ptr _makeGesture( - Device* device, T& gesture, - const std::shared_ptr& parent) { - return parent->make_interface::type>( - device, std::forward(gesture), parent); - } -} - -std::shared_ptr Gesture::makeGesture( - Device* device, config::Gesture& gesture, - const std::shared_ptr& parent) { - return std::visit([&device, &parent](auto&& x) { - return _makeGesture(device, x, parent); - }, gesture); -} - -std::shared_ptr Gesture::makeGesture( - Device* device, const std::string& type, - config::Gesture& config, - const std::shared_ptr& parent) { - if (type == AxisGesture::interface_name) { - config = config::AxisGesture(); - } else if (type == IntervalGesture::interface_name) { - config = config::IntervalGesture(); - } else if (type == ReleaseGesture::interface_name) { - config = config::ReleaseGesture(); - } else if (type == ThresholdGesture::interface_name) { - config = config::ThresholdGesture(); - } else if (type == NullGesture::interface_name) { - config = config::NoGesture(); - } else { - throw InvalidGesture(); - } - - return makeGesture(device, config, parent); -} +/* + * Copyright 2019-2023 PixlOne + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * 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 + +using namespace logid; +using namespace logid::actions; + +Gesture::Gesture(Device* device, + std::shared_ptr node, + const std::string& name, tables t) : + ipcgull::interface(SERVICE_ROOT_NAME ".Gesture." + name, std::move(t)), + _node(std::move(node)), _device(device) { +} + +namespace { + template + struct gesture_type { + typedef typename T::gesture type; + }; + + template + struct gesture_type : gesture_type { + }; + + template + struct gesture_type : gesture_type { + }; + + template + std::shared_ptr _makeGesture( + Device* device, T& gesture, + const std::shared_ptr& parent) { + return parent->make_interface::type>( + device, std::forward(gesture), parent); + } +} + +std::shared_ptr Gesture::makeGesture( + Device* device, config::Gesture& gesture, + const std::shared_ptr& parent) { + return std::visit([&device, &parent](auto&& x) { + return _makeGesture(device, x, parent); + }, gesture); +} + +std::shared_ptr Gesture::makeGesture( + Device* device, const std::string& type, + config::Gesture& config, + const std::shared_ptr& parent) { + if (type == AxisGesture::interface_name) { + config = config::AxisGesture(); + } else if (type == IntervalGesture::interface_name) { + config = config::IntervalGesture(); + } else if (type == ReleaseGesture::interface_name) { + config = config::ReleaseGesture(); + } else if (type == ThresholdGesture::interface_name) { + config = config::ThresholdGesture(); + } else if (type == NullGesture::interface_name) { + config = config::NoGesture(); + } else { + throw InvalidGesture(); + } + + return makeGesture(device, config, parent); +} diff --git a/src/logid/actions/gesture/Gesture.h b/src/logid/actions/gesture/Gesture.h index 13822b6..86b11a5 100644 --- a/src/logid/actions/gesture/Gesture.h +++ b/src/logid/actions/gesture/Gesture.h @@ -1,73 +1,73 @@ -/* - * Copyright 2019-2023 PixlOne - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * 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 -#include - -namespace logid::actions { - class InvalidGesture : public std::exception { - public: - explicit InvalidGesture(std::string what = "") : _what(std::move(what)) { - } - - [[nodiscard]] const char* what() const noexcept override { - return _what.c_str(); - } - - private: - std::string _what; - }; - - class Gesture : public ipcgull::interface { - public: - virtual void press(bool init_threshold) = 0; - - virtual void release(bool primary) = 0; - - virtual void move(int16_t axis) = 0; - - [[nodiscard]] virtual bool wheelCompatibility() const = 0; - - [[nodiscard]] virtual bool metThreshold() const = 0; - - virtual ~Gesture() = default; - - static std::shared_ptr makeGesture(Device* device, - config::Gesture& gesture, - const std::shared_ptr& parent); - - static std::shared_ptr makeGesture( - Device* device, const std::string& type, - config::Gesture& gesture, - const std::shared_ptr& parent); - - protected: - Gesture(Device* device, - std::shared_ptr parent, - const std::string& name, tables t = {}); - - mutable std::shared_mutex _config_mutex; - - const std::shared_ptr _node; - Device* _device; - }; -} - -#endif //LOGID_ACTION_GESTURE_H +/* + * Copyright 2019-2023 PixlOne + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * 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 +#include + +namespace logid::actions { + class InvalidGesture : public std::exception { + public: + explicit InvalidGesture(std::string what = "") : _what(std::move(what)) { + } + + [[nodiscard]] const char* what() const noexcept override { + return _what.c_str(); + } + + private: + std::string _what; + }; + + class Gesture : public ipcgull::interface { + public: + virtual void press(bool init_threshold) = 0; + + virtual void release(bool primary) = 0; + + virtual void move(int16_t axis) = 0; + + [[nodiscard]] virtual bool wheelCompatibility() const = 0; + + [[nodiscard]] virtual bool metThreshold() const = 0; + + virtual ~Gesture() = default; + + static std::shared_ptr makeGesture(Device* device, + config::Gesture& gesture, + const std::shared_ptr& parent); + + static std::shared_ptr makeGesture( + Device* device, const std::string& type, + config::Gesture& gesture, + const std::shared_ptr& parent); + + protected: + Gesture(Device* device, + std::shared_ptr parent, + const std::string& name, tables t = {}); + + mutable std::shared_mutex _config_mutex; + + const std::shared_ptr _node; + Device* _device; + }; +} + +#endif //LOGID_ACTION_GESTURE_H diff --git a/src/logid/actions/gesture/IntervalGesture.cpp b/src/logid/actions/gesture/IntervalGesture.cpp index 48c61aa..eac56d9 100644 --- a/src/logid/actions/gesture/IntervalGesture.cpp +++ b/src/logid/actions/gesture/IntervalGesture.cpp @@ -1,117 +1,117 @@ -/* - * Copyright 2019-2023 PixlOne - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * 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 - -using namespace logid::actions; - -const char* IntervalGesture::interface_name = "OnInterval"; - -IntervalGesture::IntervalGesture( - Device* device, config::IntervalGesture& config, - const std::shared_ptr& parent) : - Gesture(device, parent, interface_name, { - { - {"GetConfig", {this, &IntervalGesture::getConfig, {"interval", "threshold"}}}, - {"SetInterval", {this, &IntervalGesture::setInterval, {"interval"}}}, - {"SetThreshold", {this, &IntervalGesture::setThreshold, {"interval"}}}, - {"SetAction", {this, &IntervalGesture::setAction, {"type"}}} - }, - {}, - {} - }), - _axis(0), _interval_pass_count(0), _config(config) { - if (config.action) { - try { - _action = Action::makeAction(device, config.action.value(), _node); - } catch (InvalidAction& e) { - logPrintf(WARN, "Mapping gesture to invalid action"); - } - } -} - -void IntervalGesture::press(bool init_threshold) { - std::shared_lock lock(_config_mutex); - if (init_threshold) { - _axis = (int32_t) _config.threshold.value_or(defaults::gesture_threshold); - } else { - _axis = 0; - } - _interval_pass_count = 0; -} - -void IntervalGesture::release([[maybe_unused]] bool primary) { -} - -void IntervalGesture::move(int16_t axis) { - std::shared_lock lock(_config_mutex); - if (!_config.interval.has_value()) - return; - - const auto threshold = - _config.threshold.value_or(defaults::gesture_threshold); - _axis += axis; - if (_axis < threshold) - return; - - int32_t new_interval_count = (_axis - threshold) / _config.interval.value(); - if (new_interval_count > _interval_pass_count) { - if (_action) { - _action->press(); - _action->release(); - } - } - _interval_pass_count = new_interval_count; -} - -bool IntervalGesture::wheelCompatibility() const { - return true; -} - -bool IntervalGesture::metThreshold() const { - std::shared_lock lock(_config_mutex); - return _axis >= _config.threshold.value_or(defaults::gesture_threshold); -} - -std::tuple IntervalGesture::getConfig() const { - std::shared_lock lock(_config_mutex); - return {_config.interval.value_or(0), _config.threshold.value_or(0)}; -} - -void IntervalGesture::setInterval(int interval) { - std::unique_lock lock(_config_mutex); - if (interval == 0) - _config.interval.reset(); - else - _config.interval = interval; -} - -void IntervalGesture::setThreshold(int threshold) { - std::unique_lock lock(_config_mutex); - if (threshold == 0) - _config.threshold.reset(); - else - _config.threshold = threshold; -} - -void IntervalGesture::setAction(const std::string& type) { - std::unique_lock lock(_config_mutex); - _action.reset(); - _action = Action::makeAction(_device, type, _config.action, _node); -} +/* + * Copyright 2019-2023 PixlOne + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * 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 + +using namespace logid::actions; + +const char* IntervalGesture::interface_name = "OnInterval"; + +IntervalGesture::IntervalGesture( + Device* device, config::IntervalGesture& config, + const std::shared_ptr& parent) : + Gesture(device, parent, interface_name, { + { + {"GetConfig", {this, &IntervalGesture::getConfig, {"interval", "threshold"}}}, + {"SetInterval", {this, &IntervalGesture::setInterval, {"interval"}}}, + {"SetThreshold", {this, &IntervalGesture::setThreshold, {"interval"}}}, + {"SetAction", {this, &IntervalGesture::setAction, {"type"}}} + }, + {}, + {} + }), + _axis(0), _interval_pass_count(0), _config(config) { + if (config.action) { + try { + _action = Action::makeAction(device, config.action.value(), _node); + } catch (InvalidAction& e) { + logPrintf(WARN, "Mapping gesture to invalid action"); + } + } +} + +void IntervalGesture::press(bool init_threshold) { + std::shared_lock lock(_config_mutex); + if (init_threshold) { + _axis = (int32_t) _config.threshold.value_or(defaults::gesture_threshold); + } else { + _axis = 0; + } + _interval_pass_count = 0; +} + +void IntervalGesture::release([[maybe_unused]] bool primary) { +} + +void IntervalGesture::move(int16_t axis) { + std::shared_lock lock(_config_mutex); + if (!_config.interval.has_value()) + return; + + const auto threshold = + _config.threshold.value_or(defaults::gesture_threshold); + _axis += axis; + if (_axis < threshold) + return; + + int32_t new_interval_count = (_axis - threshold) / _config.interval.value(); + if (new_interval_count > _interval_pass_count) { + if (_action) { + _action->press(); + _action->release(); + } + } + _interval_pass_count = new_interval_count; +} + +bool IntervalGesture::wheelCompatibility() const { + return true; +} + +bool IntervalGesture::metThreshold() const { + std::shared_lock lock(_config_mutex); + return _axis >= _config.threshold.value_or(defaults::gesture_threshold); +} + +std::tuple IntervalGesture::getConfig() const { + std::shared_lock lock(_config_mutex); + return {_config.interval.value_or(0), _config.threshold.value_or(0)}; +} + +void IntervalGesture::setInterval(int interval) { + std::unique_lock lock(_config_mutex); + if (interval == 0) + _config.interval.reset(); + else + _config.interval = interval; +} + +void IntervalGesture::setThreshold(int threshold) { + std::unique_lock lock(_config_mutex); + if (threshold == 0) + _config.threshold.reset(); + else + _config.threshold = threshold; +} + +void IntervalGesture::setAction(const std::string& type) { + std::unique_lock lock(_config_mutex); + _action.reset(); + _action = Action::makeAction(_device, type, _config.action, _node); +} diff --git a/src/logid/actions/gesture/IntervalGesture.h b/src/logid/actions/gesture/IntervalGesture.h index 434e192..a79f9a6 100644 --- a/src/logid/actions/gesture/IntervalGesture.h +++ b/src/logid/actions/gesture/IntervalGesture.h @@ -1,58 +1,58 @@ -/* - * Copyright 2019-2023 PixlOne - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * 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 - -namespace logid::actions { - class IntervalGesture : public Gesture { - public: - static const char* interface_name; - - IntervalGesture(Device* device, config::IntervalGesture& config, - const std::shared_ptr& parent); - - void press(bool init_threshold) final; - - void release(bool primary) final; - - void move(int16_t axis) final; - - [[nodiscard]] bool wheelCompatibility() const final; - - [[nodiscard]] bool metThreshold() const final; - - [[nodiscard]] std::tuple getConfig() const; - - void setInterval(int interval); - - void setThreshold(int threshold); - - void setAction(const std::string& type); - - protected: - int32_t _axis; - int32_t _interval_pass_count; - std::shared_ptr _action; - config::IntervalGesture& _config; - private: - }; -} - -#endif //LOGID_ACTION_INTERVALGESTURE_H +/* + * Copyright 2019-2023 PixlOne + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * 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 + +namespace logid::actions { + class IntervalGesture : public Gesture { + public: + static const char* interface_name; + + IntervalGesture(Device* device, config::IntervalGesture& config, + const std::shared_ptr& parent); + + void press(bool init_threshold) final; + + void release(bool primary) final; + + void move(int16_t axis) final; + + [[nodiscard]] bool wheelCompatibility() const final; + + [[nodiscard]] bool metThreshold() const final; + + [[nodiscard]] std::tuple getConfig() const; + + void setInterval(int interval); + + void setThreshold(int threshold); + + void setAction(const std::string& type); + + protected: + int32_t _axis; + int32_t _interval_pass_count; + std::shared_ptr _action; + config::IntervalGesture& _config; + private: + }; +} + +#endif //LOGID_ACTION_INTERVALGESTURE_H diff --git a/src/logid/actions/gesture/NullGesture.cpp b/src/logid/actions/gesture/NullGesture.cpp index 9cdb39b..cf9c576 100644 --- a/src/logid/actions/gesture/NullGesture.cpp +++ b/src/logid/actions/gesture/NullGesture.cpp @@ -1,51 +1,51 @@ -/* - * Copyright 2019-2023 PixlOne - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * 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 - -using namespace logid::actions; - -const char* NullGesture::interface_name = "None"; - -NullGesture::NullGesture(Device* device, - config::NoGesture& config, - const std::shared_ptr& parent) : - Gesture(device, parent, interface_name), _config(config) { -} - -void NullGesture::press(bool init_threshold) { - _axis = init_threshold ? _config.threshold.value_or( - defaults::gesture_threshold) : 0; -} - -void NullGesture::release(bool primary) { - // Do nothing - (void) primary; // Suppress unused warning -} - -void NullGesture::move(int16_t axis) { - _axis += axis; -} - -bool NullGesture::wheelCompatibility() const { - return true; -} - -bool NullGesture::metThreshold() const { - return _axis >= _config.threshold.value_or(defaults::gesture_threshold); +/* + * Copyright 2019-2023 PixlOne + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * 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 + +using namespace logid::actions; + +const char* NullGesture::interface_name = "None"; + +NullGesture::NullGesture(Device* device, + config::NoGesture& config, + const std::shared_ptr& parent) : + Gesture(device, parent, interface_name), _config(config) { +} + +void NullGesture::press(bool init_threshold) { + _axis = init_threshold ? _config.threshold.value_or( + defaults::gesture_threshold) : 0; +} + +void NullGesture::release(bool primary) { + // Do nothing + (void) primary; // Suppress unused warning +} + +void NullGesture::move(int16_t axis) { + _axis += axis; +} + +bool NullGesture::wheelCompatibility() const { + return true; +} + +bool NullGesture::metThreshold() const { + return _axis >= _config.threshold.value_or(defaults::gesture_threshold); } \ No newline at end of file diff --git a/src/logid/actions/gesture/NullGesture.h b/src/logid/actions/gesture/NullGesture.h index 181ff37..182b8c7 100644 --- a/src/logid/actions/gesture/NullGesture.h +++ b/src/logid/actions/gesture/NullGesture.h @@ -1,48 +1,48 @@ -/* - * Copyright 2019-2023 PixlOne - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * 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 - -namespace logid::actions { - class NullGesture : public Gesture { - public: - static const char* interface_name; - - NullGesture(Device* device, - config::NoGesture& config, - const std::shared_ptr& parent); - - void press(bool init_threshold) final; - - void release(bool primary) final; - - void move(int16_t axis) final; - - [[nodiscard]] bool wheelCompatibility() const final; - - [[nodiscard]] bool metThreshold() const final; - - protected: - int32_t _axis{}; - config::NoGesture& _config; - }; -} - -#endif //LOGID_ACTION_NULLGESTURE_H +/* + * Copyright 2019-2023 PixlOne + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * 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 + +namespace logid::actions { + class NullGesture : public Gesture { + public: + static const char* interface_name; + + NullGesture(Device* device, + config::NoGesture& config, + const std::shared_ptr& parent); + + void press(bool init_threshold) final; + + void release(bool primary) final; + + void move(int16_t axis) final; + + [[nodiscard]] bool wheelCompatibility() const final; + + [[nodiscard]] bool metThreshold() const final; + + protected: + int32_t _axis{}; + config::NoGesture& _config; + }; +} + +#endif //LOGID_ACTION_NULLGESTURE_H diff --git a/src/logid/actions/gesture/ReleaseGesture.cpp b/src/logid/actions/gesture/ReleaseGesture.cpp index 1d0b54c..ab52e8c 100644 --- a/src/logid/actions/gesture/ReleaseGesture.cpp +++ b/src/logid/actions/gesture/ReleaseGesture.cpp @@ -1,89 +1,89 @@ -/* - * Copyright 2019-2023 PixlOne - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * 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 - -using namespace logid::actions; - -const char* ReleaseGesture::interface_name = "OnRelease"; - -ReleaseGesture::ReleaseGesture(Device* device, config::ReleaseGesture& config, - const std::shared_ptr& parent) : - Gesture(device, parent, interface_name, { - { - {"GetThreshold", {this, &ReleaseGesture::getThreshold, {"threshold"}}}, - {"SetThreshold", {this, &ReleaseGesture::setThreshold, {"threshold"}}}, - {"SetAction", {this, &ReleaseGesture::setAction, {"type"}}} - }, - {}, - {} - }), _config(config) { - if (_config.action.has_value()) - _action = Action::makeAction(device, _config.action.value(), _node); -} - -void ReleaseGesture::press(bool init_threshold) { - std::shared_lock lock(_config_mutex); - if (init_threshold) { - _axis = (int32_t) (_config.threshold.value_or(defaults::gesture_threshold)); - } else { - _axis = 0; - } -} - -void ReleaseGesture::release(bool primary) { - if (metThreshold() && primary) { - if (_action) { - _action->press(); - _action->release(); - } - } -} - -void ReleaseGesture::move(int16_t axis) { - _axis += axis; -} - -bool ReleaseGesture::wheelCompatibility() const { - return false; -} - -bool ReleaseGesture::metThreshold() const { - std::shared_lock lock(_config_mutex); - return _axis >= _config.threshold.value_or(defaults::gesture_threshold); -} - - -int ReleaseGesture::getThreshold() const { - std::shared_lock lock(_config_mutex); - return _config.threshold.value_or(0); -} - -void ReleaseGesture::setThreshold(int threshold) { - std::unique_lock lock(_config_mutex); - if (threshold == 0) - _config.threshold.reset(); - else - _config.threshold = threshold; -} - -void ReleaseGesture::setAction(const std::string& type) { - std::unique_lock lock(_config_mutex); - _action.reset(); - _action = Action::makeAction(_device, type, _config.action, _node); -} +/* + * Copyright 2019-2023 PixlOne + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * 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 + +using namespace logid::actions; + +const char* ReleaseGesture::interface_name = "OnRelease"; + +ReleaseGesture::ReleaseGesture(Device* device, config::ReleaseGesture& config, + const std::shared_ptr& parent) : + Gesture(device, parent, interface_name, { + { + {"GetThreshold", {this, &ReleaseGesture::getThreshold, {"threshold"}}}, + {"SetThreshold", {this, &ReleaseGesture::setThreshold, {"threshold"}}}, + {"SetAction", {this, &ReleaseGesture::setAction, {"type"}}} + }, + {}, + {} + }), _config(config) { + if (_config.action.has_value()) + _action = Action::makeAction(device, _config.action.value(), _node); +} + +void ReleaseGesture::press(bool init_threshold) { + std::shared_lock lock(_config_mutex); + if (init_threshold) { + _axis = (int32_t) (_config.threshold.value_or(defaults::gesture_threshold)); + } else { + _axis = 0; + } +} + +void ReleaseGesture::release(bool primary) { + if (metThreshold() && primary) { + if (_action) { + _action->press(); + _action->release(); + } + } +} + +void ReleaseGesture::move(int16_t axis) { + _axis += axis; +} + +bool ReleaseGesture::wheelCompatibility() const { + return false; +} + +bool ReleaseGesture::metThreshold() const { + std::shared_lock lock(_config_mutex); + return _axis >= _config.threshold.value_or(defaults::gesture_threshold); +} + + +int ReleaseGesture::getThreshold() const { + std::shared_lock lock(_config_mutex); + return _config.threshold.value_or(0); +} + +void ReleaseGesture::setThreshold(int threshold) { + std::unique_lock lock(_config_mutex); + if (threshold == 0) + _config.threshold.reset(); + else + _config.threshold = threshold; +} + +void ReleaseGesture::setAction(const std::string& type) { + std::unique_lock lock(_config_mutex); + _action.reset(); + _action = Action::makeAction(_device, type, _config.action, _node); +} diff --git a/src/logid/actions/gesture/ReleaseGesture.h b/src/logid/actions/gesture/ReleaseGesture.h index f417d83..12101ee 100644 --- a/src/logid/actions/gesture/ReleaseGesture.h +++ b/src/logid/actions/gesture/ReleaseGesture.h @@ -1,54 +1,54 @@ -/* - * Copyright 2019-2023 PixlOne - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * 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 - -namespace logid::actions { - class ReleaseGesture : public Gesture { - public: - static const char* interface_name; - - ReleaseGesture(Device* device, config::ReleaseGesture& config, - const std::shared_ptr& parent); - - void press(bool init_threshold) final; - - void release(bool primary) final; - - void move(int16_t axis) final; - - [[nodiscard]] bool wheelCompatibility() const final; - - [[nodiscard]] bool metThreshold() const final; - - [[nodiscard]] int getThreshold() const; - - void setThreshold(int threshold); - - void setAction(const std::string& type); - - protected: - int32_t _axis{}; - std::shared_ptr _action; - config::ReleaseGesture& _config; - }; -} - -#endif //LOGID_ACTION_RELEASEGESTURE_H +/* + * Copyright 2019-2023 PixlOne + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * 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 + +namespace logid::actions { + class ReleaseGesture : public Gesture { + public: + static const char* interface_name; + + ReleaseGesture(Device* device, config::ReleaseGesture& config, + const std::shared_ptr& parent); + + void press(bool init_threshold) final; + + void release(bool primary) final; + + void move(int16_t axis) final; + + [[nodiscard]] bool wheelCompatibility() const final; + + [[nodiscard]] bool metThreshold() const final; + + [[nodiscard]] int getThreshold() const; + + void setThreshold(int threshold); + + void setAction(const std::string& type); + + protected: + int32_t _axis{}; + std::shared_ptr _action; + config::ReleaseGesture& _config; + }; +} + +#endif //LOGID_ACTION_RELEASEGESTURE_H diff --git a/src/logid/actions/gesture/ThresholdGesture.cpp b/src/logid/actions/gesture/ThresholdGesture.cpp index 5dbaba8..b0385ee 100644 --- a/src/logid/actions/gesture/ThresholdGesture.cpp +++ b/src/logid/actions/gesture/ThresholdGesture.cpp @@ -1,95 +1,95 @@ -/* - * Copyright 2019-2023 PixlOne, michtere - * - * 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 - -using namespace logid::actions; - -const char* ThresholdGesture::interface_name = "OnRelease"; - -ThresholdGesture::ThresholdGesture( - Device* device, config::ThresholdGesture& config, - const std::shared_ptr& parent) : - Gesture(device, parent, interface_name, { - { - {"GetThreshold", {this, &ThresholdGesture::getThreshold, {"threshold"}}}, - {"SetThreshold", {this, &ThresholdGesture::setThreshold, {"threshold"}}}, - {"SetAction", {this, &ThresholdGesture::setAction, {"type"}}} - }, - {}, - {} - }), _config(config) { - if (config.action) { - try { - _action = Action::makeAction(device, config.action.value(), _node); - } catch (InvalidAction& e) { - logPrintf(WARN, "Mapping gesture to invalid action"); - } - } -} - -void ThresholdGesture::press(bool init_threshold) { - std::shared_lock lock(_config_mutex); - _axis = init_threshold ? (int32_t) _config.threshold.value_or(defaults::gesture_threshold) : 0; - this->_executed = false; -} - -void ThresholdGesture::release([[maybe_unused]] bool primary) { - this->_executed = false; -} - -void ThresholdGesture::move(int16_t axis) { - _axis += axis; - - if (!this->_executed && metThreshold()) { - if (_action) { - _action->press(); - _action->release(); - } - this->_executed = true; - } -} - -bool ThresholdGesture::metThreshold() const { - std::shared_lock lock(_config_mutex); - return _axis >= _config.threshold.value_or(defaults::gesture_threshold); -} - -bool ThresholdGesture::wheelCompatibility() const { - return false; -} - -int ThresholdGesture::getThreshold() const { - std::shared_lock lock(_config_mutex); - return _config.threshold.value_or(0); -} - -void ThresholdGesture::setThreshold(int threshold) { - std::unique_lock lock(_config_mutex); - if (threshold == 0) - _config.threshold.reset(); - else - _config.threshold = threshold; -} - -void ThresholdGesture::setAction(const std::string& type) { - std::unique_lock lock(_config_mutex); - _action.reset(); - _action = Action::makeAction(_device, type, _config.action, _node); -} +/* + * Copyright 2019-2023 PixlOne, michtere + * + * 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 + +using namespace logid::actions; + +const char* ThresholdGesture::interface_name = "OnRelease"; + +ThresholdGesture::ThresholdGesture( + Device* device, config::ThresholdGesture& config, + const std::shared_ptr& parent) : + Gesture(device, parent, interface_name, { + { + {"GetThreshold", {this, &ThresholdGesture::getThreshold, {"threshold"}}}, + {"SetThreshold", {this, &ThresholdGesture::setThreshold, {"threshold"}}}, + {"SetAction", {this, &ThresholdGesture::setAction, {"type"}}} + }, + {}, + {} + }), _config(config) { + if (config.action) { + try { + _action = Action::makeAction(device, config.action.value(), _node); + } catch (InvalidAction& e) { + logPrintf(WARN, "Mapping gesture to invalid action"); + } + } +} + +void ThresholdGesture::press(bool init_threshold) { + std::shared_lock lock(_config_mutex); + _axis = init_threshold ? (int32_t) _config.threshold.value_or(defaults::gesture_threshold) : 0; + this->_executed = false; +} + +void ThresholdGesture::release([[maybe_unused]] bool primary) { + this->_executed = false; +} + +void ThresholdGesture::move(int16_t axis) { + _axis += axis; + + if (!this->_executed && metThreshold()) { + if (_action) { + _action->press(); + _action->release(); + } + this->_executed = true; + } +} + +bool ThresholdGesture::metThreshold() const { + std::shared_lock lock(_config_mutex); + return _axis >= _config.threshold.value_or(defaults::gesture_threshold); +} + +bool ThresholdGesture::wheelCompatibility() const { + return false; +} + +int ThresholdGesture::getThreshold() const { + std::shared_lock lock(_config_mutex); + return _config.threshold.value_or(0); +} + +void ThresholdGesture::setThreshold(int threshold) { + std::unique_lock lock(_config_mutex); + if (threshold == 0) + _config.threshold.reset(); + else + _config.threshold = threshold; +} + +void ThresholdGesture::setAction(const std::string& type) { + std::unique_lock lock(_config_mutex); + _action.reset(); + _action = Action::makeAction(_device, type, _config.action, _node); +} diff --git a/src/logid/actions/gesture/ThresholdGesture.h b/src/logid/actions/gesture/ThresholdGesture.h index c5a90f1..a64f8fa 100644 --- a/src/logid/actions/gesture/ThresholdGesture.h +++ b/src/logid/actions/gesture/ThresholdGesture.h @@ -1,57 +1,57 @@ -/* - * Copyright 2019-2023 PixlOne, michtere - * - * 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_THRESHOLDGESTURE_H -#define LOGID_ACTION_THRESHOLDGESTURE_H - -#include - -namespace logid::actions { - class ThresholdGesture : public Gesture { - public: - static const char* interface_name; - - ThresholdGesture(Device* device, config::ThresholdGesture& config, - const std::shared_ptr& parent); - - void press(bool init_threshold) final; - - void release(bool primary) final; - - void move(int16_t axis) final; - - [[nodiscard]] bool metThreshold() const final; - - [[nodiscard]] bool wheelCompatibility() const final; - - [[nodiscard]] int getThreshold() const; - - void setThreshold(int threshold); - - void setAction(const std::string& type); - - protected: - int32_t _axis{}; - std::shared_ptr _action; - config::ThresholdGesture& _config; - - private: - bool _executed = false; - }; -} - -#endif //LOGID_ACTION_THRESHOLDGESTURE_H +/* + * Copyright 2019-2023 PixlOne, michtere + * + * 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_THRESHOLDGESTURE_H +#define LOGID_ACTION_THRESHOLDGESTURE_H + +#include + +namespace logid::actions { + class ThresholdGesture : public Gesture { + public: + static const char* interface_name; + + ThresholdGesture(Device* device, config::ThresholdGesture& config, + const std::shared_ptr& parent); + + void press(bool init_threshold) final; + + void release(bool primary) final; + + void move(int16_t axis) final; + + [[nodiscard]] bool metThreshold() const final; + + [[nodiscard]] bool wheelCompatibility() const final; + + [[nodiscard]] int getThreshold() const; + + void setThreshold(int threshold); + + void setAction(const std::string& type); + + protected: + int32_t _axis{}; + std::shared_ptr _action; + config::ThresholdGesture& _config; + + private: + bool _executed = false; + }; +} + +#endif //LOGID_ACTION_THRESHOLDGESTURE_H diff --git a/src/logid/backend/Error.cpp b/src/logid/backend/Error.cpp index 6623e30..299ef68 100644 --- a/src/logid/backend/Error.cpp +++ b/src/logid/backend/Error.cpp @@ -1,29 +1,29 @@ -/* - * Copyright 2019-2023 PixlOne - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * 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 - -using namespace logid::backend; - -const char* DeviceNotReady::what() const noexcept { - return "device not ready"; -} - -const char* TimeoutError::what() const noexcept { - return "Device timed out"; -} +/* + * Copyright 2019-2023 PixlOne + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * 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 + +using namespace logid::backend; + +const char* DeviceNotReady::what() const noexcept { + return "device not ready"; +} + +const char* TimeoutError::what() const noexcept { + return "Device timed out"; +} diff --git a/src/logid/backend/Error.h b/src/logid/backend/Error.h index 520923e..87214a3 100644 --- a/src/logid/backend/Error.h +++ b/src/logid/backend/Error.h @@ -1,38 +1,38 @@ -/* - * Copyright 2019-2023 PixlOne - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * 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 - -#include - -namespace logid::backend { - class DeviceNotReady : public std::exception { - public: - [[nodiscard]] const char* what() const noexcept override; - }; - - class TimeoutError : public std::exception { - public: - TimeoutError() = default; - - [[nodiscard]] const char* what() const noexcept override; - }; -} - +/* + * Copyright 2019-2023 PixlOne + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * 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 + +#include + +namespace logid::backend { + class DeviceNotReady : public std::exception { + public: + [[nodiscard]] const char* what() const noexcept override; + }; + + class TimeoutError : public std::exception { + public: + TimeoutError() = default; + + [[nodiscard]] const char* what() const noexcept override; + }; +} + #endif //LOGID_BACKEND_ERROR_H \ No newline at end of file diff --git a/src/logid/backend/EventHandlerList.h b/src/logid/backend/EventHandlerList.h index be67143..0829cf5 100644 --- a/src/logid/backend/EventHandlerList.h +++ b/src/logid/backend/EventHandlerList.h @@ -1,137 +1,137 @@ -/* - * Copyright 2019-2023 PixlOne - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * 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_EVENTHANDLERLIST_H -#define LOGID_EVENTHANDLERLIST_H - -#include -#include -#include -#include -#include - -template -class EventHandlerLock; - -template -class EventHandlerList { -public: - typedef std::list> list_t; - typedef typename list_t::iterator iterator_t; -private: - list_t list; - std::shared_mutex mutex; - std::shared_mutex add_mutex; - - void cleanup() { - std::unique_lock lock(mutex, std::try_to_lock); - if (lock.owns_lock()) { - std::list to_remove; - for (auto it = list.begin(); it != list.end(); ++it) { - if (!it->second) - to_remove.push_back(it); - } - - for(auto& it : to_remove) - list.erase(it); - } - } -public: - iterator_t add(typename T::EventHandler handler) { - std::unique_lock add_lock(add_mutex); - list.emplace_front(std::move(handler), true); - return list.begin(); - } - - void remove(iterator_t iterator) { - std::unique_lock lock(mutex, std::try_to_lock); - if (lock.owns_lock()) { - std::unique_lock add_lock(add_mutex); - list.erase(iterator); - } else { - iterator->second = false; - } - } - - template - void run_all(Arg arg) { - cleanup(); - std::shared_lock lock(mutex); - std::shared_lock add_lock(add_mutex); - for (auto& handler : list) { - add_lock.unlock(); - if (handler.second) { - if (handler.first.condition(arg)) - handler.first.callback(arg); - } - add_lock.lock(); - } - } -}; - -template -class EventHandlerLock { - typedef EventHandlerList list_t; - typedef typename list_t::iterator_t iterator_t; - - friend T; - - std::weak_ptr _list; - iterator_t _iterator; - - EventHandlerLock(const std::shared_ptr& list, iterator_t iterator) : - _list (list), _iterator (iterator) { - } -public: - EventHandlerLock() = default; - - EventHandlerLock(const EventHandlerLock&) = delete; - - EventHandlerLock(EventHandlerLock&& o) noexcept : _list (o._list), _iterator (o._iterator) { - o._list.reset(); - } - - EventHandlerLock& operator=(const EventHandlerLock&) = delete; - - EventHandlerLock& operator=(EventHandlerLock&& o) noexcept { - if (this != &o) { - if (auto list = _list.lock()) { - this->_list.reset(); - list->remove(_iterator); - } - - this->_list = o._list; - o._list.reset(); - this->_iterator = o._iterator; - } - - return *this; - } - - ~EventHandlerLock() { - if(auto list = _list.lock()) - list->remove(_iterator); - } - - [[nodiscard]] bool empty() const noexcept { - return _list.expired(); - } -}; - -#endif //LOGID_EVENTHANDLERLIST_H +/* + * Copyright 2019-2023 PixlOne + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * 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_EVENTHANDLERLIST_H +#define LOGID_EVENTHANDLERLIST_H + +#include +#include +#include +#include +#include + +template +class EventHandlerLock; + +template +class EventHandlerList { +public: + typedef std::list> list_t; + typedef typename list_t::iterator iterator_t; +private: + list_t list; + std::shared_mutex mutex; + std::shared_mutex add_mutex; + + void cleanup() { + std::unique_lock lock(mutex, std::try_to_lock); + if (lock.owns_lock()) { + std::list to_remove; + for (auto it = list.begin(); it != list.end(); ++it) { + if (!it->second) + to_remove.push_back(it); + } + + for(auto& it : to_remove) + list.erase(it); + } + } +public: + iterator_t add(typename T::EventHandler handler) { + std::unique_lock add_lock(add_mutex); + list.emplace_front(std::move(handler), true); + return list.begin(); + } + + void remove(iterator_t iterator) { + std::unique_lock lock(mutex, std::try_to_lock); + if (lock.owns_lock()) { + std::unique_lock add_lock(add_mutex); + list.erase(iterator); + } else { + iterator->second = false; + } + } + + template + void run_all(Arg arg) { + cleanup(); + std::shared_lock lock(mutex); + std::shared_lock add_lock(add_mutex); + for (auto& handler : list) { + add_lock.unlock(); + if (handler.second) { + if (handler.first.condition(arg)) + handler.first.callback(arg); + } + add_lock.lock(); + } + } +}; + +template +class EventHandlerLock { + typedef EventHandlerList list_t; + typedef typename list_t::iterator_t iterator_t; + + friend T; + + std::weak_ptr _list; + iterator_t _iterator; + + EventHandlerLock(const std::shared_ptr& list, iterator_t iterator) : + _list (list), _iterator (iterator) { + } +public: + EventHandlerLock() = default; + + EventHandlerLock(const EventHandlerLock&) = delete; + + EventHandlerLock(EventHandlerLock&& o) noexcept : _list (o._list), _iterator (o._iterator) { + o._list.reset(); + } + + EventHandlerLock& operator=(const EventHandlerLock&) = delete; + + EventHandlerLock& operator=(EventHandlerLock&& o) noexcept { + if (this != &o) { + if (auto list = _list.lock()) { + this->_list.reset(); + list->remove(_iterator); + } + + this->_list = o._list; + o._list.reset(); + this->_iterator = o._iterator; + } + + return *this; + } + + ~EventHandlerLock() { + if(auto list = _list.lock()) + list->remove(_iterator); + } + + [[nodiscard]] bool empty() const noexcept { + return _list.expired(); + } +}; + +#endif //LOGID_EVENTHANDLERLIST_H diff --git a/src/logid/backend/hidpp/Device.cpp b/src/logid/backend/hidpp/Device.cpp index f3bf362..ee171b6 100644 --- a/src/logid/backend/hidpp/Device.cpp +++ b/src/logid/backend/hidpp/Device.cpp @@ -1,320 +1,320 @@ -/* - * Copyright 2019-2023 PixlOne - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * 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 - -using namespace logid::backend; -using namespace logid::backend::hidpp; - -using namespace std::chrono; - -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"; - case VirtualNode: - return "Virtual device"; - default: - return "Invalid device"; - } -} - -Device::InvalidDevice::Reason Device::InvalidDevice::code() const noexcept { - return _reason; -} - -Device::Device(const std::string& path, DeviceIndex index, - const std::shared_ptr& monitor, double timeout) : - io_timeout(duration_cast( - duration(timeout))), - _raw_device(raw::RawDevice::make(path, monitor)), - _receiver(nullptr), _path(path), _index(index) { -} - -Device::Device(std::shared_ptr raw_device, DeviceIndex index, - double timeout) : - io_timeout(duration_cast( - duration(timeout))), - _raw_device(std::move(raw_device)), _receiver(nullptr), - _path(_raw_device->rawPath()), _index(index) { -} - -Device::Device(const std::shared_ptr& receiver, - hidpp::DeviceConnectionEvent event, double timeout) : - io_timeout(duration_cast( - duration(timeout))), - _raw_device(receiver->rawDevice()), _receiver(receiver), - _path(receiver->rawDevice()->rawPath()), _index(event.index) { - // Device will throw an error soon, just do it now - if (!event.linkEstablished) - throw InvalidDevice(InvalidDevice::Asleep); - - if (!event.fromTimeoutCheck) - _pid = event.pid; - else - _pid = receiver->getPairingInfo(_index).pid; -} - -Device::Device(const std::shared_ptr& receiver, - DeviceIndex index, double timeout) : - io_timeout(duration_cast( - duration(timeout))), - _raw_device(receiver->rawDevice()), - _receiver(receiver), _path(receiver->rawDevice()->rawPath()), - _index(index) { - _pid = receiver->getPairingInfo(_index).pid; -} - -const std::string& Device::devicePath() const { - return _path; -} - -DeviceIndex Device::deviceIndex() const { - return _index; -} - -const std::tuple& Device::version() const { - return _version; -} - - -void Device::_setupReportsAndInit() { - _event_handlers = std::make_shared>(); - - supported_reports = getSupportedReports(_raw_device->reportDescriptor()); - if (!supported_reports) - throw InvalidDevice(InvalidDevice::NoHIDPPReport); - - /* hid_logitech_dj creates virtual /dev/hidraw nodes for receiver - * devices. We should ignore these devices. - */ - if (_raw_device->isSubDevice()) - throw InvalidDevice(InvalidDevice::VirtualNode); - - _raw_handler = _raw_device->addEventHandler( - {[index = _index]( - const std::vector& report) -> bool { - return (report[Offset::Type] == Report::Type::Short || - report[Offset::Type] == Report::Type::Long) && - (report[Offset::DeviceIndex] == index); - }, - [self_weak = _self](const std::vector& report) -> void { - Report _report(report); - if(auto self = self_weak.lock()) - self->handleEvent(_report); - }}); - - _init(); -} - -void Device::_init() { - try { - hidpp20::Root 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; - - // HID++ 2.0 is not supported, assume HID++ 1.0 - _version = std::make_tuple(1, 0); - } catch (hidpp20::Error& e) { - /* Should never happen, device not ready? */ - throw DeviceNotReady(); - } - - /* Do a stability test before going further */ - if (std::get<0>(_version) >= 2) { - if (!isStable20()) { - throw DeviceNotReady(); - } - } else { - if (!isStable10()) { - throw DeviceNotReady(); - } - } - - if (!_receiver) { - _pid = _raw_device->productId(); - if (std::get<0>(_version) >= 2) { - try { - hidpp20::DeviceName deviceName(this); - _name = deviceName.getName(); - } catch (hidpp20::UnsupportedFeature& e) { - _name = _raw_device->name(); - } - } else { - _name = _raw_device->name(); - } - } else { - if (std::get<0>(_version) >= 2) { - try { - hidpp20::DeviceName deviceName(this); - _name = deviceName.getName(); - } catch (hidpp20::UnsupportedFeature& e) { - _name = _receiver->getDeviceName(_index); - } - } else { - _name = _receiver->getDeviceName(_index); - } - } -} - -EventHandlerLock Device::addEventHandler(EventHandler handler) { - return {_event_handlers, _event_handlers->add(std::move(handler))}; -} - -void Device::handleEvent(Report& report) { - if (responseReport(report)) - return; - - _event_handlers->run_all(report); -} - -Report Device::sendReport(const Report& report) { - /* Must complete transaction before next send */ - std::lock_guard send_lock(_send_mutex); - _sent_sub_id = report.subId(); - _sent_address = report.address(); - std::unique_lock lock(_response_mutex); - _sendReport(report); - - bool valid = _response_cv.wait_for( - lock, io_timeout, [this]() { - return _response.has_value(); - }); - - if (!valid) { - _sent_sub_id.reset(); - throw TimeoutError(); - } - - Response response = _response.value(); - _response.reset(); - _sent_sub_id.reset(); - _sent_address.reset(); - - if (std::holds_alternative(response)) { - return std::get(response); - } else if (std::holds_alternative(response)) { - auto error = std::get(response); - throw hidpp10::Error(error.error_code, error.device_index); - } else if (std::holds_alternative(response)) { - auto error = std::get(response); - throw hidpp20::Error(error.error_code, error.device_index); - } - - // Should not be reached - throw std::runtime_error("unhandled variant type"); -} - -bool Device::responseReport(const Report& report) { - std::lock_guard lock(_response_mutex); - Response response = report; - uint8_t sub_id; - uint8_t address; - - Report::Hidpp10Error hidpp10_error{}; - Report::Hidpp20Error hidpp20_error{}; - if (report.isError10(hidpp10_error)) { - sub_id = hidpp10_error.sub_id; - address = hidpp10_error.address; - response = hidpp10_error; - } else if (report.isError20(hidpp20_error)) { - sub_id = hidpp20_error.feature_index; - address = (hidpp20_error.function << 4) | (hidpp20_error.software_id & 0x0f); - } else { - sub_id = report.subId(); - address = report.address(); - } - - if (sub_id == _sent_sub_id && address == _sent_address) { - _response = response; - _response_cv.notify_all(); - return true; - } else { - return false; - } - -} - -const std::shared_ptr& Device::rawDevice() const { - return _raw_device; -} - -void Device::_sendReport(Report report) { - reportFixup(report); - _raw_device->sendReport(report.rawReport()); -} - -void Device::sendReportNoACK(const Report& report) { - std::lock_guard lock(_send_mutex); - _sendReport(report); -} - -bool Device::isStable10() { - return true; -} - -bool Device::isStable20() { - static const std::string ping_seq = "hello"; - - hidpp20::Root root(this); - - try { - for (auto c: ping_seq) { - if (root.ping(c) != c) - return false; - } - } catch (std::exception& e) { - return false; - } - - return true; -} - -void Device::reportFixup(Report& report) const { - switch (report.type()) { - case Report::Type::Short: - if (!(supported_reports & ShortReportSupported)) - report.setType(Report::Type::Long); - break; - case Report::Type::Long: - /* Report can be truncated, but that isn't a good idea. */ - assert(supported_reports & LongReportSupported); - } -} - -const std::string& Device::name() const { - return _name; -} - -uint16_t Device::pid() const { - return _pid; -} +/* + * Copyright 2019-2023 PixlOne + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * 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 + +using namespace logid::backend; +using namespace logid::backend::hidpp; + +using namespace std::chrono; + +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"; + case VirtualNode: + return "Virtual device"; + default: + return "Invalid device"; + } +} + +Device::InvalidDevice::Reason Device::InvalidDevice::code() const noexcept { + return _reason; +} + +Device::Device(const std::string& path, DeviceIndex index, + const std::shared_ptr& monitor, double timeout) : + io_timeout(duration_cast( + duration(timeout))), + _raw_device(raw::RawDevice::make(path, monitor)), + _receiver(nullptr), _path(path), _index(index) { +} + +Device::Device(std::shared_ptr raw_device, DeviceIndex index, + double timeout) : + io_timeout(duration_cast( + duration(timeout))), + _raw_device(std::move(raw_device)), _receiver(nullptr), + _path(_raw_device->rawPath()), _index(index) { +} + +Device::Device(const std::shared_ptr& receiver, + hidpp::DeviceConnectionEvent event, double timeout) : + io_timeout(duration_cast( + duration(timeout))), + _raw_device(receiver->rawDevice()), _receiver(receiver), + _path(receiver->rawDevice()->rawPath()), _index(event.index) { + // Device will throw an error soon, just do it now + if (!event.linkEstablished) + throw InvalidDevice(InvalidDevice::Asleep); + + if (!event.fromTimeoutCheck) + _pid = event.pid; + else + _pid = receiver->getPairingInfo(_index).pid; +} + +Device::Device(const std::shared_ptr& receiver, + DeviceIndex index, double timeout) : + io_timeout(duration_cast( + duration(timeout))), + _raw_device(receiver->rawDevice()), + _receiver(receiver), _path(receiver->rawDevice()->rawPath()), + _index(index) { + _pid = receiver->getPairingInfo(_index).pid; +} + +const std::string& Device::devicePath() const { + return _path; +} + +DeviceIndex Device::deviceIndex() const { + return _index; +} + +const std::tuple& Device::version() const { + return _version; +} + + +void Device::_setupReportsAndInit() { + _event_handlers = std::make_shared>(); + + supported_reports = getSupportedReports(_raw_device->reportDescriptor()); + if (!supported_reports) + throw InvalidDevice(InvalidDevice::NoHIDPPReport); + + /* hid_logitech_dj creates virtual /dev/hidraw nodes for receiver + * devices. We should ignore these devices. + */ + if (_raw_device->isSubDevice()) + throw InvalidDevice(InvalidDevice::VirtualNode); + + _raw_handler = _raw_device->addEventHandler( + {[index = _index]( + const std::vector& report) -> bool { + return (report[Offset::Type] == Report::Type::Short || + report[Offset::Type] == Report::Type::Long) && + (report[Offset::DeviceIndex] == index); + }, + [self_weak = _self](const std::vector& report) -> void { + Report _report(report); + if(auto self = self_weak.lock()) + self->handleEvent(_report); + }}); + + _init(); +} + +void Device::_init() { + try { + hidpp20::Root 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; + + // HID++ 2.0 is not supported, assume HID++ 1.0 + _version = std::make_tuple(1, 0); + } catch (hidpp20::Error& e) { + /* Should never happen, device not ready? */ + throw DeviceNotReady(); + } + + /* Do a stability test before going further */ + if (std::get<0>(_version) >= 2) { + if (!isStable20()) { + throw DeviceNotReady(); + } + } else { + if (!isStable10()) { + throw DeviceNotReady(); + } + } + + if (!_receiver) { + _pid = _raw_device->productId(); + if (std::get<0>(_version) >= 2) { + try { + hidpp20::DeviceName deviceName(this); + _name = deviceName.getName(); + } catch (hidpp20::UnsupportedFeature& e) { + _name = _raw_device->name(); + } + } else { + _name = _raw_device->name(); + } + } else { + if (std::get<0>(_version) >= 2) { + try { + hidpp20::DeviceName deviceName(this); + _name = deviceName.getName(); + } catch (hidpp20::UnsupportedFeature& e) { + _name = _receiver->getDeviceName(_index); + } + } else { + _name = _receiver->getDeviceName(_index); + } + } +} + +EventHandlerLock Device::addEventHandler(EventHandler handler) { + return {_event_handlers, _event_handlers->add(std::move(handler))}; +} + +void Device::handleEvent(Report& report) { + if (responseReport(report)) + return; + + _event_handlers->run_all(report); +} + +Report Device::sendReport(const Report& report) { + /* Must complete transaction before next send */ + std::lock_guard send_lock(_send_mutex); + _sent_sub_id = report.subId(); + _sent_address = report.address(); + std::unique_lock lock(_response_mutex); + _sendReport(report); + + bool valid = _response_cv.wait_for( + lock, io_timeout, [this]() { + return _response.has_value(); + }); + + if (!valid) { + _sent_sub_id.reset(); + throw TimeoutError(); + } + + Response response = _response.value(); + _response.reset(); + _sent_sub_id.reset(); + _sent_address.reset(); + + if (std::holds_alternative(response)) { + return std::get(response); + } else if (std::holds_alternative(response)) { + auto error = std::get(response); + throw hidpp10::Error(error.error_code, error.device_index); + } else if (std::holds_alternative(response)) { + auto error = std::get(response); + throw hidpp20::Error(error.error_code, error.device_index); + } + + // Should not be reached + throw std::runtime_error("unhandled variant type"); +} + +bool Device::responseReport(const Report& report) { + std::lock_guard lock(_response_mutex); + Response response = report; + uint8_t sub_id; + uint8_t address; + + Report::Hidpp10Error hidpp10_error{}; + Report::Hidpp20Error hidpp20_error{}; + if (report.isError10(hidpp10_error)) { + sub_id = hidpp10_error.sub_id; + address = hidpp10_error.address; + response = hidpp10_error; + } else if (report.isError20(hidpp20_error)) { + sub_id = hidpp20_error.feature_index; + address = (hidpp20_error.function << 4) | (hidpp20_error.software_id & 0x0f); + } else { + sub_id = report.subId(); + address = report.address(); + } + + if (sub_id == _sent_sub_id && address == _sent_address) { + _response = response; + _response_cv.notify_all(); + return true; + } else { + return false; + } + +} + +const std::shared_ptr& Device::rawDevice() const { + return _raw_device; +} + +void Device::_sendReport(Report report) { + reportFixup(report); + _raw_device->sendReport(report.rawReport()); +} + +void Device::sendReportNoACK(const Report& report) { + std::lock_guard lock(_send_mutex); + _sendReport(report); +} + +bool Device::isStable10() { + return true; +} + +bool Device::isStable20() { + static const std::string ping_seq = "hello"; + + hidpp20::Root root(this); + + try { + for (auto c: ping_seq) { + if (root.ping(c) != c) + return false; + } + } catch (std::exception& e) { + return false; + } + + return true; +} + +void Device::reportFixup(Report& report) const { + switch (report.type()) { + case Report::Type::Short: + if (!(supported_reports & ShortReportSupported)) + report.setType(Report::Type::Long); + break; + case Report::Type::Long: + /* Report can be truncated, but that isn't a good idea. */ + assert(supported_reports & LongReportSupported); + } +} + +const std::string& Device::name() const { + return _name; +} + +uint16_t Device::pid() const { + return _pid; +} diff --git a/src/logid/backend/hidpp/Device.h b/src/logid/backend/hidpp/Device.h index 38997c0..a9fa2ab 100644 --- a/src/logid/backend/hidpp/Device.h +++ b/src/logid/backend/hidpp/Device.h @@ -1,186 +1,186 @@ -/* - * Copyright 2019-2023 PixlOne - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * 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 - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace logid::backend::hidpp10 { - // Need to define here for a constructor - class Receiver; -} - -namespace logid::backend::hidpp { - struct DeviceConnectionEvent; - - template - class _deviceWrapper : public T { - friend class Device; - - public: - template - explicit _deviceWrapper(Args... args) : T(std::forward(args)...) {} - - template - static std::shared_ptr make(Args... args) { - return std::make_shared<_deviceWrapper>(std::forward(args)...); - } - }; - - class Device { - template - friend - class _deviceWrapper; - - public: - struct EventHandler { - std::function condition; - std::function callback; - }; - - class InvalidDevice : std::exception { - public: - enum Reason { - NoHIDPPReport, - InvalidRawDevice, - Asleep, - VirtualNode - }; - - explicit InvalidDevice(Reason reason) : _reason(reason) {} - - [[nodiscard]] const char* what() const noexcept override; - - [[nodiscard]] virtual Reason code() const noexcept; - - private: - Reason _reason; - }; - - [[nodiscard]] const std::string& devicePath() const; - - [[nodiscard]] DeviceIndex deviceIndex() const; - - [[nodiscard]] const std::tuple& version() const; - - [[nodiscard]] const std::string& name() const; - - [[nodiscard]] uint16_t pid() const; - - EventHandlerLock addEventHandler(EventHandler handler); - - virtual Report sendReport(const Report& report); - - virtual void sendReportNoACK(const Report& report); - - void handleEvent(Report& report); - - [[nodiscard]] const std::shared_ptr& rawDevice() const; - - Device(const Device&) = delete; - - Device(Device&&) = delete; - - virtual ~Device() = default; - - protected: - Device(const std::string& path, DeviceIndex index, - const std::shared_ptr& monitor, double timeout); - - Device(std::shared_ptr raw_device, DeviceIndex index, - double timeout); - - Device(const std::shared_ptr& receiver, - hidpp::DeviceConnectionEvent event, double timeout); - - Device(const std::shared_ptr& receiver, - DeviceIndex index, double timeout); - - // Returns whether the report is a response - virtual bool responseReport(const Report& report); - - bool isStable20(); - - bool isStable10(); - - void _sendReport(Report report); - - void reportFixup(Report& report) const; - - const std::chrono::milliseconds io_timeout; - uint8_t supported_reports{}; - - std::mutex _response_mutex; - std::condition_variable _response_cv; - private: - void _setupReportsAndInit(); - - void _init(); - - std::shared_ptr _raw_device; - EventHandlerLock _raw_handler; - std::shared_ptr _receiver; - std::string _path; - DeviceIndex _index; - - std::tuple _version; - uint16_t _pid{}; - std::string _name; - - std::mutex _send_mutex; - - typedef std::variant Response; - - std::optional _response; - std::optional _sent_sub_id{}; - std::optional _sent_address{}; - - std::shared_ptr> _event_handlers; - - std::weak_ptr _self; - - protected: - template - static std::shared_ptr makeDerived(Args... args) { - auto device = _deviceWrapper::make(std::forward(args)...); - device->_self = device; - device->_setupReportsAndInit(); - return device; - } - - public: - template - static std::shared_ptr make(Args... args) { - return makeDerived(std::forward(args)...); - } - }; - - typedef Device::EventHandler EventHandler; -} - +/* + * Copyright 2019-2023 PixlOne + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * 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 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace logid::backend::hidpp10 { + // Need to define here for a constructor + class Receiver; +} + +namespace logid::backend::hidpp { + struct DeviceConnectionEvent; + + template + class _deviceWrapper : public T { + friend class Device; + + public: + template + explicit _deviceWrapper(Args... args) : T(std::forward(args)...) {} + + template + static std::shared_ptr make(Args... args) { + return std::make_shared<_deviceWrapper>(std::forward(args)...); + } + }; + + class Device { + template + friend + class _deviceWrapper; + + public: + struct EventHandler { + std::function condition; + std::function callback; + }; + + class InvalidDevice : std::exception { + public: + enum Reason { + NoHIDPPReport, + InvalidRawDevice, + Asleep, + VirtualNode + }; + + explicit InvalidDevice(Reason reason) : _reason(reason) {} + + [[nodiscard]] const char* what() const noexcept override; + + [[nodiscard]] virtual Reason code() const noexcept; + + private: + Reason _reason; + }; + + [[nodiscard]] const std::string& devicePath() const; + + [[nodiscard]] DeviceIndex deviceIndex() const; + + [[nodiscard]] const std::tuple& version() const; + + [[nodiscard]] const std::string& name() const; + + [[nodiscard]] uint16_t pid() const; + + EventHandlerLock addEventHandler(EventHandler handler); + + virtual Report sendReport(const Report& report); + + virtual void sendReportNoACK(const Report& report); + + void handleEvent(Report& report); + + [[nodiscard]] const std::shared_ptr& rawDevice() const; + + Device(const Device&) = delete; + + Device(Device&&) = delete; + + virtual ~Device() = default; + + protected: + Device(const std::string& path, DeviceIndex index, + const std::shared_ptr& monitor, double timeout); + + Device(std::shared_ptr raw_device, DeviceIndex index, + double timeout); + + Device(const std::shared_ptr& receiver, + hidpp::DeviceConnectionEvent event, double timeout); + + Device(const std::shared_ptr& receiver, + DeviceIndex index, double timeout); + + // Returns whether the report is a response + virtual bool responseReport(const Report& report); + + bool isStable20(); + + bool isStable10(); + + void _sendReport(Report report); + + void reportFixup(Report& report) const; + + const std::chrono::milliseconds io_timeout; + uint8_t supported_reports{}; + + std::mutex _response_mutex; + std::condition_variable _response_cv; + private: + void _setupReportsAndInit(); + + void _init(); + + std::shared_ptr _raw_device; + EventHandlerLock _raw_handler; + std::shared_ptr _receiver; + std::string _path; + DeviceIndex _index; + + std::tuple _version; + uint16_t _pid{}; + std::string _name; + + std::mutex _send_mutex; + + typedef std::variant Response; + + std::optional _response; + std::optional _sent_sub_id{}; + std::optional _sent_address{}; + + std::shared_ptr> _event_handlers; + + std::weak_ptr _self; + + protected: + template + static std::shared_ptr makeDerived(Args... args) { + auto device = _deviceWrapper::make(std::forward(args)...); + device->_self = device; + device->_setupReportsAndInit(); + return device; + } + + public: + template + static std::shared_ptr make(Args... args) { + return makeDerived(std::forward(args)...); + } + }; + + typedef Device::EventHandler EventHandler; +} + #endif //LOGID_BACKEND_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 9fdb3c1..d3bacc7 100644 --- a/src/logid/backend/hidpp/Report.cpp +++ b/src/logid/backend/hidpp/Report.cpp @@ -1,297 +1,297 @@ -/* - * Copyright 2019-2023 PixlOne - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * 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 - -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(const std::vector& report_desc) { - uint8_t ret = 0; - - auto it = std::search(report_desc.begin(), report_desc.end(), - ShortReportDesc.begin(), ShortReportDesc.end()); - if (it == report_desc.end()) - it = std::search(report_desc.begin(), report_desc.end(), - ShortReportDesc2.begin(), ShortReportDesc2.end()); - if (it != report_desc.end()) - ret |= ShortReportSupported; - - it = std::search(report_desc.begin(), report_desc.end(), - LongReportDesc.begin(), LongReportDesc.end()); - if (it == report_desc.end()) - it = std::search(report_desc.begin(), report_desc.end(), - LongReportDesc2.begin(), LongReportDesc2.end()); - if (it != report_desc.end()) - ret |= LongReportSupported; - - return ret; -} - -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 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) { - 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(); - } - - _data[Offset::Type] = type; - _data[Offset::DeviceIndex] = device_index; - _data[Offset::Feature] = feature_index; - _data[Offset::Function] = (function & 0x0f) << 4 | - (sw_id & 0x0f); -} - -Report::Report(const std::vector& data) : - _data(data) { - _data.resize(HeaderLength + LongParamLength); - - // 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(); - } -} - -Report::Type Report::type() const { - return static_cast(_data[Offset::Type]); -} - -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(); - } - - _data[Offset::Type] = type; -} - -hidpp::DeviceIndex Report::deviceIndex() const { - return static_cast(_data[Offset::DeviceIndex]); -} - -[[maybe_unused]] void Report::setDeviceIndex(hidpp::DeviceIndex index) { - _data[Offset::DeviceIndex] = index; -} - -uint8_t Report::feature() const { - return _data[Offset::Feature]; -} - -[[maybe_unused]] void Report::setFeature(uint8_t feature) { - _data[Offset::Parameters] = feature; -} - -uint8_t Report::subId() const { - return _data[Offset::SubID]; -} - -[[maybe_unused]] void Report::setSubId(uint8_t sub_id) { - _data[Offset::SubID] = sub_id; -} - -uint8_t Report::function() const { - return (_data[Offset::Function] >> 4) & 0x0f; -} - -[[maybe_unused]] void Report::setFunction(uint8_t function) { - _data[Offset::Function] &= 0x0f; - _data[Offset::Function] |= (function & 0x0f) << 4; -} - -uint8_t Report::swId() const { - return _data[Offset::Function] & 0x0f; -} - -void Report::setSwId(uint8_t sw_id) { - _data[Offset::Function] &= 0xf0; - _data[Offset::Function] |= sw_id & 0x0f; -} - -uint8_t Report::address() const { - return _data[Offset::Address]; -} - -[[maybe_unused]] 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); - - for (std::size_t i = 0; i < _params.size(); i++) - _data[Offset::Parameters + i] = _params[i]; -} - -bool Report::isError10(Report::Hidpp10Error& error) const { - if (_data[Offset::Type] != Type::Short || - _data[Offset::SubID] != hidpp10::ErrorID) - return false; - - error.device_index = deviceIndex(); - error.sub_id = _data[3]; - error.address = _data[4]; - error.error_code = _data[5]; - - return true; -} - -bool Report::isError20(Report::Hidpp20Error& error) const { - if (_data[Offset::Type] != Type::Long || - _data[Offset::Feature] != hidpp20::ErrorID) - return false; - - error.device_index = deviceIndex(); - error.feature_index = _data[3]; - error.function = (_data[4] >> 4) & 0x0f; - error.software_id = _data[4] & 0x0f; - error.error_code = _data[5]; - - return true; -} - -const std::vector& Report::rawReport() const { - return _data; -} +/* + * Copyright 2019-2023 PixlOne + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * 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 + +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(const std::vector& report_desc) { + uint8_t ret = 0; + + auto it = std::search(report_desc.begin(), report_desc.end(), + ShortReportDesc.begin(), ShortReportDesc.end()); + if (it == report_desc.end()) + it = std::search(report_desc.begin(), report_desc.end(), + ShortReportDesc2.begin(), ShortReportDesc2.end()); + if (it != report_desc.end()) + ret |= ShortReportSupported; + + it = std::search(report_desc.begin(), report_desc.end(), + LongReportDesc.begin(), LongReportDesc.end()); + if (it == report_desc.end()) + it = std::search(report_desc.begin(), report_desc.end(), + LongReportDesc2.begin(), LongReportDesc2.end()); + if (it != report_desc.end()) + ret |= LongReportSupported; + + return ret; +} + +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 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) { + 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(); + } + + _data[Offset::Type] = type; + _data[Offset::DeviceIndex] = device_index; + _data[Offset::Feature] = feature_index; + _data[Offset::Function] = (function & 0x0f) << 4 | + (sw_id & 0x0f); +} + +Report::Report(const std::vector& data) : + _data(data) { + _data.resize(HeaderLength + LongParamLength); + + // 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(); + } +} + +Report::Type Report::type() const { + return static_cast(_data[Offset::Type]); +} + +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(); + } + + _data[Offset::Type] = type; +} + +hidpp::DeviceIndex Report::deviceIndex() const { + return static_cast(_data[Offset::DeviceIndex]); +} + +[[maybe_unused]] void Report::setDeviceIndex(hidpp::DeviceIndex index) { + _data[Offset::DeviceIndex] = index; +} + +uint8_t Report::feature() const { + return _data[Offset::Feature]; +} + +[[maybe_unused]] void Report::setFeature(uint8_t feature) { + _data[Offset::Parameters] = feature; +} + +uint8_t Report::subId() const { + return _data[Offset::SubID]; +} + +[[maybe_unused]] void Report::setSubId(uint8_t sub_id) { + _data[Offset::SubID] = sub_id; +} + +uint8_t Report::function() const { + return (_data[Offset::Function] >> 4) & 0x0f; +} + +[[maybe_unused]] void Report::setFunction(uint8_t function) { + _data[Offset::Function] &= 0x0f; + _data[Offset::Function] |= (function & 0x0f) << 4; +} + +uint8_t Report::swId() const { + return _data[Offset::Function] & 0x0f; +} + +void Report::setSwId(uint8_t sw_id) { + _data[Offset::Function] &= 0xf0; + _data[Offset::Function] |= sw_id & 0x0f; +} + +uint8_t Report::address() const { + return _data[Offset::Address]; +} + +[[maybe_unused]] 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); + + for (std::size_t i = 0; i < _params.size(); i++) + _data[Offset::Parameters + i] = _params[i]; +} + +bool Report::isError10(Report::Hidpp10Error& error) const { + if (_data[Offset::Type] != Type::Short || + _data[Offset::SubID] != hidpp10::ErrorID) + return false; + + error.device_index = deviceIndex(); + error.sub_id = _data[3]; + error.address = _data[4]; + error.error_code = _data[5]; + + return true; +} + +bool Report::isError20(Report::Hidpp20Error& error) const { + if (_data[Offset::Type] != Type::Long || + _data[Offset::Feature] != hidpp20::ErrorID) + return false; + + error.device_index = deviceIndex(); + error.feature_index = _data[3]; + error.function = (_data[4] >> 4) & 0x0f; + error.software_id = _data[4] & 0x0f; + error.error_code = _data[5]; + + return true; +} + +const std::vector& Report::rawReport() const { + return _data; +} diff --git a/src/logid/backend/hidpp/Report.h b/src/logid/backend/hidpp/Report.h index 305e928..e22632b 100644 --- a/src/logid/backend/hidpp/Report.h +++ b/src/logid/backend/hidpp/Report.h @@ -1,135 +1,135 @@ -/* - * Copyright 2019-2023 PixlOne - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * 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 - -#include -#include -#include - -namespace logid::backend::hidpp { - uint8_t getSupportedReports(const std::vector& report_desc); - - /* Some devices only support a subset of these reports */ - static constexpr uint8_t ShortReportSupported = 1U; - static constexpr uint8_t LongReportSupported = (1U<<1); - /* Very long reports exist, however they have not been encountered so far */ - - namespace Offset { - 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 Address = 3; - static constexpr uint8_t Function = 3; - static constexpr uint8_t Parameters = 4; - } - - class Report { - public: - typedef ReportType::ReportType Type; - - class InvalidReportID : public std::exception { - public: - InvalidReportID() = default; - - [[nodiscard]] const char* what() const noexcept override; - }; - - class InvalidReportLength : public std::exception { - public: - InvalidReportLength() = default; - - [[nodiscard]] const char* what() const noexcept override; - }; - - static constexpr std::size_t MaxDataLength = 20; - - 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); - - [[nodiscard]] Report::Type type() const; - - void setType(Report::Type type); - - [[nodiscard]] DeviceIndex deviceIndex() const; - - [[maybe_unused]] void setDeviceIndex(DeviceIndex index); - - [[nodiscard]] uint8_t feature() const; - - [[maybe_unused]] void setFeature(uint8_t feature); - - [[nodiscard]] uint8_t subId() const; - - [[maybe_unused]] void setSubId(uint8_t sub_id); - - [[nodiscard]] uint8_t function() const; - - [[maybe_unused]] void setFunction(uint8_t function); - - [[nodiscard]] uint8_t swId() const; - - void setSwId(uint8_t sw_id); - - [[nodiscard]] uint8_t address() const; - - [[maybe_unused]] void setAddress(uint8_t address); - - [[nodiscard]] std::vector::iterator paramBegin(); - - [[nodiscard]] std::vector::iterator paramEnd(); - - [[nodiscard]] std::vector::const_iterator paramBegin() const; - - [[nodiscard]] std::vector::const_iterator paramEnd() const; - - void setParams(const std::vector& _params); - - struct Hidpp10Error { - hidpp::DeviceIndex device_index; - uint8_t sub_id, address, error_code; - }; - - bool isError10(Hidpp10Error& error) const; - - struct Hidpp20Error { - hidpp::DeviceIndex device_index; - uint8_t feature_index, function, software_id, error_code; - }; - - bool isError20(Hidpp20Error& error) const; - - [[nodiscard]] const std::vector& rawReport() const; - - static constexpr std::size_t HeaderLength = 4; - private: - std::vector _data; - }; -} - +/* + * Copyright 2019-2023 PixlOne + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * 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 + +#include +#include +#include + +namespace logid::backend::hidpp { + uint8_t getSupportedReports(const std::vector& report_desc); + + /* Some devices only support a subset of these reports */ + static constexpr uint8_t ShortReportSupported = 1U; + static constexpr uint8_t LongReportSupported = (1U<<1); + /* Very long reports exist, however they have not been encountered so far */ + + namespace Offset { + 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 Address = 3; + static constexpr uint8_t Function = 3; + static constexpr uint8_t Parameters = 4; + } + + class Report { + public: + typedef ReportType::ReportType Type; + + class InvalidReportID : public std::exception { + public: + InvalidReportID() = default; + + [[nodiscard]] const char* what() const noexcept override; + }; + + class InvalidReportLength : public std::exception { + public: + InvalidReportLength() = default; + + [[nodiscard]] const char* what() const noexcept override; + }; + + static constexpr std::size_t MaxDataLength = 20; + + 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); + + [[nodiscard]] Report::Type type() const; + + void setType(Report::Type type); + + [[nodiscard]] DeviceIndex deviceIndex() const; + + [[maybe_unused]] void setDeviceIndex(DeviceIndex index); + + [[nodiscard]] uint8_t feature() const; + + [[maybe_unused]] void setFeature(uint8_t feature); + + [[nodiscard]] uint8_t subId() const; + + [[maybe_unused]] void setSubId(uint8_t sub_id); + + [[nodiscard]] uint8_t function() const; + + [[maybe_unused]] void setFunction(uint8_t function); + + [[nodiscard]] uint8_t swId() const; + + void setSwId(uint8_t sw_id); + + [[nodiscard]] uint8_t address() const; + + [[maybe_unused]] void setAddress(uint8_t address); + + [[nodiscard]] std::vector::iterator paramBegin(); + + [[nodiscard]] std::vector::iterator paramEnd(); + + [[nodiscard]] std::vector::const_iterator paramBegin() const; + + [[nodiscard]] std::vector::const_iterator paramEnd() const; + + void setParams(const std::vector& _params); + + struct Hidpp10Error { + hidpp::DeviceIndex device_index; + uint8_t sub_id, address, error_code; + }; + + bool isError10(Hidpp10Error& error) const; + + struct Hidpp20Error { + hidpp::DeviceIndex device_index; + uint8_t feature_index, function, software_id, error_code; + }; + + bool isError20(Hidpp20Error& error) const; + + [[nodiscard]] const std::vector& rawReport() const; + + static constexpr std::size_t HeaderLength = 4; + 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 da15c1c..6475e95 100644 --- a/src/logid/backend/hidpp/defs.h +++ b/src/logid/backend/hidpp/defs.h @@ -1,51 +1,51 @@ -/* - * Copyright 2019-2023 PixlOne - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * 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_DEFS_H -#define LOGID_BACKEND_HIDPP_DEFS_H - -#include - -namespace logid::backend::hidpp { - namespace ReportType { - enum ReportType : uint8_t { - Short = 0x10, - Long = 0x11 - }; - } - - enum DeviceIndex : uint8_t { - DefaultDevice = 0xff, - CordedDevice = 0, - WirelessDevice1 = 1, - WirelessDevice2 [[maybe_unused]] = 2, - WirelessDevice3 [[maybe_unused]] = 3, - WirelessDevice4 [[maybe_unused]] = 4, - WirelessDevice5 [[maybe_unused]] = 5, - WirelessDevice6 = 6, - }; - - static constexpr uint8_t softwareID = 2; - /* For sending reports with no response, use a different SW ID */ - static constexpr uint8_t noAckSoftwareID = 3; - - static constexpr std::size_t ShortParamLength = 3; - static constexpr std::size_t LongParamLength = 16; -} - +/* + * Copyright 2019-2023 PixlOne + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * 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_DEFS_H +#define LOGID_BACKEND_HIDPP_DEFS_H + +#include + +namespace logid::backend::hidpp { + namespace ReportType { + enum ReportType : uint8_t { + Short = 0x10, + Long = 0x11 + }; + } + + enum DeviceIndex : uint8_t { + DefaultDevice = 0xff, + CordedDevice = 0, + WirelessDevice1 = 1, + WirelessDevice2 [[maybe_unused]] = 2, + WirelessDevice3 [[maybe_unused]] = 3, + WirelessDevice4 [[maybe_unused]] = 4, + WirelessDevice5 [[maybe_unused]] = 5, + WirelessDevice6 = 6, + }; + + static constexpr uint8_t softwareID = 2; + /* For sending reports with no response, use a different SW ID */ + static constexpr uint8_t noAckSoftwareID = 3; + + static constexpr std::size_t ShortParamLength = 3; + static constexpr std::size_t LongParamLength = 16; +} + #endif //LOGID_BACKEND_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 f661107..08f7cda 100644 --- a/src/logid/backend/hidpp10/Device.cpp +++ b/src/logid/backend/hidpp10/Device.cpp @@ -1,164 +1,164 @@ -/* - * Copyright 2019-2023 PixlOne - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * 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 - -using namespace logid::backend; -using namespace logid::backend::hidpp10; - -hidpp::Report setupRegReport(hidpp::DeviceIndex index, - uint8_t sub_id, uint8_t address, - const std::vector& params) { - hidpp::Report::Type type = params.size() <= hidpp::ShortParamLength ? - hidpp::Report::Type::Short : hidpp::Report::Type::Long; - - if (sub_id == SetRegisterLong) { - // When setting a long register, the report must be long. - type = hidpp::Report::Type::Long; - } - - hidpp::Report request(type, index, sub_id, address); - std::copy(params.begin(), params.end(), request.paramBegin()); - - return request; -} - -Device::Device(const std::string& path, hidpp::DeviceIndex index, - const std::shared_ptr& monitor, double timeout) : - hidpp::Device(path, index, monitor, timeout) { -} - -Device::Device(std::shared_ptr raw_dev, hidpp::DeviceIndex index, - double timeout) : hidpp::Device(std::move(raw_dev), index, timeout) { -} - -Device::Device(const std::shared_ptr& receiver, - hidpp::DeviceIndex index, - double timeout) - : hidpp::Device(receiver, index, timeout) { -} - -void Device::ResponseSlot::reset() { - response.reset(); - sub_id.reset(); -} - -hidpp::Report Device::sendReport(const hidpp::Report& report) { - auto& response_slot = _responses[report.subId() % SubIDCount]; - - std::unique_lock lock(_response_mutex); - _response_cv.wait(lock, [&response_slot]() { - return !response_slot.sub_id.has_value(); - }); - response_slot.sub_id = report.subId(); - - _sendReport(report); - bool valid = _response_cv.wait_for(lock, io_timeout, [&response_slot]() { - return response_slot.response.has_value(); - }); - - if (!valid) { - response_slot.reset(); - throw TimeoutError(); - } - - auto response = response_slot.response.value(); - response_slot.reset(); - - if (std::holds_alternative(response)) { - return std::get(response); - } else { // if(std::holds_alternative(response)) - auto error = std::get(response); - throw Error(error.error_code, error.device_index); - } -} - -bool Device::responseReport(const hidpp::Report& report) { - std::lock_guard lock(_response_mutex); - uint8_t sub_id; - - bool is_error = false; - hidpp::Report::Hidpp10Error hidpp10_error{}; - if (report.isError10(hidpp10_error)) { - sub_id = hidpp10_error.sub_id; - is_error = true; - } else { - sub_id = report.subId(); - } - - auto& response_slot = _responses[sub_id % SubIDCount]; - - if (!response_slot.sub_id.has_value() || response_slot.sub_id.value() != sub_id) - return false; - - if (is_error) { - response_slot.response = hidpp10_error; - } else { - response_slot.response = report; - } - - _response_cv.notify_all(); - return true; -} - -std::vector Device::getRegister(uint8_t address, - const std::vector& params, - hidpp::Report::Type type) { - assert(params.size() <= hidpp::LongParamLength); - - 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, - hidpp::Report::Type type) { - assert(params.size() <= hidpp::LongParamLength); - - uint8_t sub_id = type == hidpp::Report::Type::Short ? - SetRegisterShort : SetRegisterLong; - - return accessRegister(sub_id, address, params); -} - -void Device::setRegisterNoResponse(uint8_t address, - const std::vector& params, - hidpp::Report::Type type) { - assert(params.size() <= hidpp::LongParamLength); - - uint8_t sub_id = type == hidpp::Report::Type::Short ? - SetRegisterShort : SetRegisterLong; - - return accessRegisterNoResponse(sub_id, address, params); -} - -std::vector Device::accessRegister(uint8_t sub_id, uint8_t address, - const std::vector& params) { - auto response = sendReport(setupRegReport(deviceIndex(), sub_id, address, params)); - return {response.paramBegin(), response.paramEnd()}; -} - -void Device::accessRegisterNoResponse(uint8_t sub_id, uint8_t address, - const std::vector& params) { - sendReportNoACK(setupRegReport(deviceIndex(), sub_id, address, params)); -} +/* + * Copyright 2019-2023 PixlOne + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * 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 + +using namespace logid::backend; +using namespace logid::backend::hidpp10; + +hidpp::Report setupRegReport(hidpp::DeviceIndex index, + uint8_t sub_id, uint8_t address, + const std::vector& params) { + hidpp::Report::Type type = params.size() <= hidpp::ShortParamLength ? + hidpp::Report::Type::Short : hidpp::Report::Type::Long; + + if (sub_id == SetRegisterLong) { + // When setting a long register, the report must be long. + type = hidpp::Report::Type::Long; + } + + hidpp::Report request(type, index, sub_id, address); + std::copy(params.begin(), params.end(), request.paramBegin()); + + return request; +} + +Device::Device(const std::string& path, hidpp::DeviceIndex index, + const std::shared_ptr& monitor, double timeout) : + hidpp::Device(path, index, monitor, timeout) { +} + +Device::Device(std::shared_ptr raw_dev, hidpp::DeviceIndex index, + double timeout) : hidpp::Device(std::move(raw_dev), index, timeout) { +} + +Device::Device(const std::shared_ptr& receiver, + hidpp::DeviceIndex index, + double timeout) + : hidpp::Device(receiver, index, timeout) { +} + +void Device::ResponseSlot::reset() { + response.reset(); + sub_id.reset(); +} + +hidpp::Report Device::sendReport(const hidpp::Report& report) { + auto& response_slot = _responses[report.subId() % SubIDCount]; + + std::unique_lock lock(_response_mutex); + _response_cv.wait(lock, [&response_slot]() { + return !response_slot.sub_id.has_value(); + }); + response_slot.sub_id = report.subId(); + + _sendReport(report); + bool valid = _response_cv.wait_for(lock, io_timeout, [&response_slot]() { + return response_slot.response.has_value(); + }); + + if (!valid) { + response_slot.reset(); + throw TimeoutError(); + } + + auto response = response_slot.response.value(); + response_slot.reset(); + + if (std::holds_alternative(response)) { + return std::get(response); + } else { // if(std::holds_alternative(response)) + auto error = std::get(response); + throw Error(error.error_code, error.device_index); + } +} + +bool Device::responseReport(const hidpp::Report& report) { + std::lock_guard lock(_response_mutex); + uint8_t sub_id; + + bool is_error = false; + hidpp::Report::Hidpp10Error hidpp10_error{}; + if (report.isError10(hidpp10_error)) { + sub_id = hidpp10_error.sub_id; + is_error = true; + } else { + sub_id = report.subId(); + } + + auto& response_slot = _responses[sub_id % SubIDCount]; + + if (!response_slot.sub_id.has_value() || response_slot.sub_id.value() != sub_id) + return false; + + if (is_error) { + response_slot.response = hidpp10_error; + } else { + response_slot.response = report; + } + + _response_cv.notify_all(); + return true; +} + +std::vector Device::getRegister(uint8_t address, + const std::vector& params, + hidpp::Report::Type type) { + assert(params.size() <= hidpp::LongParamLength); + + 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, + hidpp::Report::Type type) { + assert(params.size() <= hidpp::LongParamLength); + + uint8_t sub_id = type == hidpp::Report::Type::Short ? + SetRegisterShort : SetRegisterLong; + + return accessRegister(sub_id, address, params); +} + +void Device::setRegisterNoResponse(uint8_t address, + const std::vector& params, + hidpp::Report::Type type) { + assert(params.size() <= hidpp::LongParamLength); + + uint8_t sub_id = type == hidpp::Report::Type::Short ? + SetRegisterShort : SetRegisterLong; + + return accessRegisterNoResponse(sub_id, address, params); +} + +std::vector Device::accessRegister(uint8_t sub_id, uint8_t address, + const std::vector& params) { + auto response = sendReport(setupRegReport(deviceIndex(), sub_id, address, params)); + return {response.paramBegin(), response.paramEnd()}; +} + +void Device::accessRegisterNoResponse(uint8_t sub_id, uint8_t address, + const std::vector& params) { + sendReportNoACK(setupRegReport(deviceIndex(), sub_id, address, params)); +} diff --git a/src/logid/backend/hidpp10/Device.h b/src/logid/backend/hidpp10/Device.h index 0876d54..61ed1d6 100644 --- a/src/logid/backend/hidpp10/Device.h +++ b/src/logid/backend/hidpp10/Device.h @@ -1,94 +1,94 @@ -/* - * Copyright 2019-2023 PixlOne - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * 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 - -#include -#include -#include -#include -#include - -namespace logid::backend::hidpp10 { - class Device : public hidpp::Device { - public: - - hidpp::Report sendReport(const hidpp::Report& report) final; - - std::vector getRegister(uint8_t address, - const std::vector& params, - hidpp::Report::Type type); - - std::vector setRegister(uint8_t address, - const std::vector& params, - hidpp::Report::Type type); - - void setRegisterNoResponse(uint8_t address, const std::vector& params, - hidpp::Report::Type type); - - protected: - Device(const std::string& path, hidpp::DeviceIndex index, - const std::shared_ptr& monitor, double timeout); - - Device(std::shared_ptr raw_dev, - hidpp::DeviceIndex index, double timeout); - - Device(const std::shared_ptr& receiver, - hidpp::DeviceIndex index, double timeout); - - bool responseReport(const hidpp::Report& report) final; - - private: - typedef std::variant Response; - - struct ResponseSlot { - std::optional response; - std::optional sub_id; - - void reset(); - }; - - std::array _responses; - - std::vector accessRegister( - uint8_t sub_id, uint8_t address, const std::vector& params); - - void accessRegisterNoResponse( - uint8_t sub_id, uint8_t address, const std::vector& params); - - protected: - template - static std::shared_ptr makeDerived(Args... args) { - auto device = hidpp::Device::makeDerived(std::forward(args)...); - - if (std::get<0>(device->version()) != 1) - throw std::invalid_argument("not a hid++ 1.0 device"); - - return device; - } - - public: - template - static std::shared_ptr make(Args... args) { - return makeDerived(std::forward(args)...); - } - }; -} - +/* + * Copyright 2019-2023 PixlOne + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * 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 + +#include +#include +#include +#include +#include + +namespace logid::backend::hidpp10 { + class Device : public hidpp::Device { + public: + + hidpp::Report sendReport(const hidpp::Report& report) final; + + std::vector getRegister(uint8_t address, + const std::vector& params, + hidpp::Report::Type type); + + std::vector setRegister(uint8_t address, + const std::vector& params, + hidpp::Report::Type type); + + void setRegisterNoResponse(uint8_t address, const std::vector& params, + hidpp::Report::Type type); + + protected: + Device(const std::string& path, hidpp::DeviceIndex index, + const std::shared_ptr& monitor, double timeout); + + Device(std::shared_ptr raw_dev, + hidpp::DeviceIndex index, double timeout); + + Device(const std::shared_ptr& receiver, + hidpp::DeviceIndex index, double timeout); + + bool responseReport(const hidpp::Report& report) final; + + private: + typedef std::variant Response; + + struct ResponseSlot { + std::optional response; + std::optional sub_id; + + void reset(); + }; + + std::array _responses; + + std::vector accessRegister( + uint8_t sub_id, uint8_t address, const std::vector& params); + + void accessRegisterNoResponse( + uint8_t sub_id, uint8_t address, const std::vector& params); + + protected: + template + static std::shared_ptr makeDerived(Args... args) { + auto device = hidpp::Device::makeDerived(std::forward(args)...); + + if (std::get<0>(device->version()) != 1) + throw std::invalid_argument("not a hid++ 1.0 device"); + + return device; + } + + public: + template + static std::shared_ptr make(Args... args) { + return makeDerived(std::forward(args)...); + } + }; +} + #endif //LOGID_BACKEND_HIDPP10_DEVICE_H \ No newline at end of file diff --git a/src/logid/backend/hidpp10/Error.cpp b/src/logid/backend/hidpp10/Error.cpp index fd145e2..1356396 100644 --- a/src/logid/backend/hidpp10/Error.cpp +++ b/src/logid/backend/hidpp10/Error.cpp @@ -1,68 +1,68 @@ -/* - * Copyright 2019-2023 PixlOne - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * 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 - -using namespace logid::backend; -using namespace logid::backend::hidpp10; - -Error::Error(uint8_t code, hidpp::DeviceIndex index) : _code(code), _index(index) { - 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 "Unknown error code"; - } -} - -uint8_t Error::code() const noexcept { - return _code; -} - -hidpp::DeviceIndex Error::deviceIndex() const noexcept { - return _index; -} +/* + * Copyright 2019-2023 PixlOne + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * 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 + +using namespace logid::backend; +using namespace logid::backend::hidpp10; + +Error::Error(uint8_t code, hidpp::DeviceIndex index) : _code(code), _index(index) { + 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 "Unknown error code"; + } +} + +uint8_t Error::code() const noexcept { + return _code; +} + +hidpp::DeviceIndex Error::deviceIndex() const noexcept { + return _index; +} diff --git a/src/logid/backend/hidpp10/Error.h b/src/logid/backend/hidpp10/Error.h index d172061..e04702c 100644 --- a/src/logid/backend/hidpp10/Error.h +++ b/src/logid/backend/hidpp10/Error.h @@ -1,61 +1,61 @@ -/* - * Copyright 2019-2023 PixlOne - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * 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 - -#include -#include -#include - -namespace logid::backend::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, hidpp::DeviceIndex index); - - [[nodiscard]] const char* what() const noexcept override; - - [[nodiscard]] uint8_t code() const noexcept; - - [[nodiscard]] hidpp::DeviceIndex deviceIndex() const noexcept; - - private: - uint8_t _code; - hidpp::DeviceIndex _index; - }; -} - +/* + * Copyright 2019-2023 PixlOne + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * 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 + +#include +#include +#include + +namespace logid::backend::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, hidpp::DeviceIndex index); + + [[nodiscard]] const char* what() const noexcept override; + + [[nodiscard]] uint8_t code() const noexcept; + + [[nodiscard]] hidpp::DeviceIndex deviceIndex() const noexcept; + + private: + uint8_t _code; + hidpp::DeviceIndex _index; + }; +} + #endif //LOGID_BACKEND_HIDPP10_ERROR_H \ No newline at end of file diff --git a/src/logid/backend/hidpp10/Receiver.cpp b/src/logid/backend/hidpp10/Receiver.cpp index 9cefc76..312fbe8 100644 --- a/src/logid/backend/hidpp10/Receiver.cpp +++ b/src/logid/backend/hidpp10/Receiver.cpp @@ -1,380 +1,380 @@ -/* - * Copyright 2019-2023 PixlOne - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * 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 - -using namespace logid::backend::hidpp10; -using namespace logid::backend; - -const char* InvalidReceiver::what() const noexcept { - return "Not a receiver"; -} - -Receiver::Receiver(const std::string& path, const std::shared_ptr& monitor, - double timeout) : Device(path, hidpp::DefaultDevice, monitor, timeout) { -} - -void Receiver::_receiverCheck() { - // Check if the device is a receiver - try { - getNotificationFlags(); - } catch (hidpp10::Error& e) { - if (e.code() == Error::InvalidAddress) - throw InvalidReceiver(); - } - - // TODO: is there a better way of checking if this is a bolt receiver? - _is_bolt = pid() == 0xc548; -} - -Receiver::NotificationFlags Receiver::getNotificationFlags() { - auto response = getRegister(EnableHidppNotifications, {}, hidpp::ReportType::Short); - - NotificationFlags flags{}; - flags.deviceBatteryStatus = response[0] & (1 << 4); - flags.receiverWirelessNotifications = response[1] & (1 << 0); - flags.receiverSoftwarePresent = response[1] & (1 << 3); - - return flags; -} - -void Receiver::setNotifications(NotificationFlags flags) { - std::vector request(3); - - if (flags.deviceBatteryStatus) - request[0] |= (1 << 4); - if (flags.receiverWirelessNotifications) - request[1] |= 1; - if (flags.receiverSoftwarePresent) - request[1] |= (1 << 3); - - setRegister(EnableHidppNotifications, request, hidpp::ReportType::Short); -} - -void Receiver::enumerate() { - setRegisterNoResponse(ConnectionState, {2}, hidpp::ReportType::Short); -} - -///TODO: Investigate usage -uint8_t Receiver::getConnectionState(hidpp::DeviceIndex index) { - auto response = getRegister(ConnectionState, {index}, hidpp::ReportType::Short); - - return response[0]; -} - -void Receiver::startPairing(uint8_t timeout) { - std::vector request(3); - - if (_is_bolt) - throw std::invalid_argument("unifying pairing on bolt"); - - request[0] = 1; - request[1] = hidpp::DefaultDevice; - request[2] = timeout; - - if (_is_bolt) { - setRegister(BoltDevicePairing, request, hidpp::ReportType::Long); - } else { - setRegister(DevicePairing, request, hidpp::ReportType::Short); - } -} - -// bolt pairing request from solaar -void Receiver::startBoltPairing(const DeviceDiscoveryEvent& discovery) { - std::vector request(10); - - request[0] = 1; // start pair - request[1] = 0; // slot, from solaar. what does this mean? - - for(int i = 0; i < 6; ++i) - request[2 + i] = (discovery.address >> (i*8)) & 0xff; - - request[8] = discovery.authentication; - - // TODO: what does entropy do? - request[9] = (discovery.deviceType == hidpp::DeviceKeyboard) ? 10 : 20; - - setRegister(BoltDevicePairing, request, hidpp::ReportType::Long); -} - -void Receiver::stopPairing() { - std::vector request(3); - - request[0] = 2; - request[1] = hidpp::DefaultDevice; - - if (_is_bolt) - setRegister(BoltDevicePairing, request, hidpp::ReportType::Long); - else - setRegister(DevicePairing, request, hidpp::ReportType::Short); -} - -void Receiver::startDiscover(uint8_t timeout) { - std::vector request = {timeout, 1}; - - if (!_is_bolt) - throw std::invalid_argument("not a bolt receiver"); - - setRegister(BoltDeviceDiscovery, request, hidpp::ReportType::Short); -} - -void Receiver::stopDiscover() { - std::vector request = {0, 2}; - - if (!_is_bolt) - throw std::invalid_argument("not a bolt receiver"); - - setRegister(BoltDeviceDiscovery, request, hidpp::ReportType::Short); -} - -void Receiver::disconnect(hidpp::DeviceIndex index) { - std::vector request(2); - - request[0] = _is_bolt ? 3 : 2; - request[1] = index; - - if (_is_bolt) - setRegister(BoltDevicePairing, request, hidpp::ReportType::Long); - else - setRegister(DevicePairing, request, hidpp::ReportType::Short); -} - -std::map Receiver::getDeviceActivity() { - auto response = getRegister(DeviceActivity, {}, hidpp::ReportType::Long); - - std::map device_activity; - for (uint8_t i = hidpp::WirelessDevice1; i <= hidpp::WirelessDevice6; i++) - device_activity[static_cast(i)] = response[i]; - - return device_activity; -} - -struct Receiver::PairingInfo -Receiver::getPairingInfo(hidpp::DeviceIndex index) { - std::vector request(1); - request[0] = index; - if (_is_bolt) - request[0] += 0x50; - else - request[0] += 0x1f; - - auto response = getRegister(PairingInfo, request, hidpp::ReportType::Long); - - struct PairingInfo info{}; - if (_is_bolt) { - info = { - .destinationId = 0, // no longer given? - .reportInterval = 0, // no longer given? - .pid = (uint16_t) ((response[3] << 8) | response[2]), - .deviceType = static_cast(response[1]) - }; - } else { - info = { - .destinationId = response[1], - .reportInterval = response[2], - .pid = (uint16_t) ((response[3] << 8) | response[4]), - .deviceType = static_cast(response[7]) - }; - } - - - return info; -} - -struct Receiver::ExtendedPairingInfo -Receiver::getExtendedPairingInfo(hidpp::DeviceIndex index) { - const int device_num_offset = _is_bolt ? 0x50 : 0x2f; - const int serial_num_offset = _is_bolt ? 4 : 1; - const int report_offset = _is_bolt ? 8 : 5; - const int psl_offset = _is_bolt ? 12 : 8; - - std::vector request(1, index + device_num_offset); - - auto response = getRegister(PairingInfo, request, hidpp::ReportType::Long); - - ExtendedPairingInfo info{}; - - info.serialNumber = 0; - for (uint8_t i = 0; i < 4; i++) - info.serialNumber |= (response[i + serial_num_offset] << 8 * i); - - for (uint8_t i = 0; i < 4; i++) - info.reportTypes[i] = response[i + report_offset]; - - uint8_t psl = response[psl_offset] & 0xf; - if (psl > 0xc) - info.powerSwitchLocation = PowerSwitchLocation::Reserved; - else - info.powerSwitchLocation = static_cast(psl); - - return info; -} - -std::string Receiver::getDeviceName(hidpp::DeviceIndex index) { - std::vector request(2); - std::string name; - request[0] = index; - if (_is_bolt) { - /* Undocumented, deduced the following - * param 1 refers to part of string, 1-indexed - * - * response at 0x01 is [reg] [param 1] [size] [str...] - * response at 0x02-... is [next part of str...] - */ - request[0] += 0x60; - request[1] = 0x01; - - auto resp = getRegister(PairingInfo, request, hidpp::ReportType::Long); - const uint8_t size = resp[2]; - const uint8_t chunk_size = resp.size() - 3; - const uint8_t chunks = size / chunk_size + (size % chunk_size ? 1 : 0); - - name.resize(size, ' '); - for (int i = 0; i < chunks; ++i) { - for (int j = 0; j < chunk_size; ++j) { - name[i * chunk_size + j] = (char) resp[j + 3]; - } - - if (i < chunks - 1) { - request[1] = i + 1; - resp = getRegister(PairingInfo, request, hidpp::ReportType::Long); - } - } - - } else { - request[0] += 0x3f; - - auto response = getRegister(PairingInfo, request, hidpp::ReportType::Long); - - const uint8_t size = response[1]; - - name.resize(size, ' '); - for (std::size_t i = 0; i < size && i + 2 < response.size(); i++) - name[i] = (char) (response[i + 2]); - - } - - return name; -} - -hidpp::DeviceIndex Receiver::deviceDisconnectionEvent(const hidpp::Report& report) { - assert(report.subId() == DeviceDisconnection); - return report.deviceIndex(); -} - -hidpp::DeviceConnectionEvent Receiver::deviceConnectionEvent(const hidpp::Report& report) { - assert(report.subId() == DeviceConnection); - - auto data = report.paramBegin(); - - return { - .index = report.deviceIndex(), - .pid = (uint16_t) ((data[2] << 8) | data[1]), - .deviceType = static_cast(data[0] & 0x0f), - .unifying = ((report.address() & 0b111) == 0x04), - .softwarePresent = bool(data[0] & (1 << 4)), - .encrypted = (bool) (data[0] & (1 << 5)), - .linkEstablished = !(data[0] & (1 << 6)), - .withPayload = (bool) (data[0] & (1 << 7)), - - .fromTimeoutCheck = false, - }; -} - -bool Receiver::fillDeviceDiscoveryEvent(DeviceDiscoveryEvent& event, - const hidpp::Report& report) { - assert(report.subId() == DeviceDiscovered); - - auto data = report.paramBegin(); - - if (data[1] == 0) { - // device discovery event - - uint64_t address = 0 ; - for (int i = 0; i < 6; ++i) - address |= ((uint64_t)(data[6 + i]) << (8*i)); - - event.deviceType = static_cast(data[3]); - event.pid = (data[5] << 8) | data[4]; - event.address = address; - event.authentication = data[14]; - event.seq_num = report.address(); - event.name = ""; - - return false; - } else { - /* bad sequence, do not continue */ - if (event.seq_num != report.address()) - return false; - - const int block_size = hidpp::LongParamLength - 3; - - if (data[1] == 1) { - event.name.resize(data[2], ' '); - } - - for(int i = 0; i < block_size; ++i) { - const size_t j = (data[1]-1)*block_size + i; - if (j < event.name.size()) { - event.name[j] = (char)data[i + 3]; - } else { - return true; - } - } - - return false; - } -} - -PairStatusEvent Receiver::pairStatusEvent(const hidpp::Report& report) { - assert(report.subId() == PairStatus); - - return { - .pairing = (bool)(report.paramBegin()[0] & 1), - .error = static_cast(report.paramBegin()[1]) - }; -} - -BoltPairStatusEvent Receiver::boltPairStatusEvent(const hidpp::Report& report) { - assert(report.subId() == BoltPairStatus); - - return { - .pairing = report.address() == 0, - .error = report.paramBegin()[1] - }; -} - -DiscoveryStatusEvent Receiver::discoveryStatusEvent(const hidpp::Report& report) { - assert(report.subId() == DiscoveryStatus); - - return { - .discovering = report.address() == 0, - .error = report.paramBegin()[1] - }; -} - -std::string Receiver::passkeyEvent(const hidpp::Report& report) { - assert(report.subId() == PasskeyRequest); - - return {report.paramBegin(), report.paramBegin() + 6}; -} - -bool Receiver::bolt() const { - return _is_bolt; -} +/* + * Copyright 2019-2023 PixlOne + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * 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 + +using namespace logid::backend::hidpp10; +using namespace logid::backend; + +const char* InvalidReceiver::what() const noexcept { + return "Not a receiver"; +} + +Receiver::Receiver(const std::string& path, const std::shared_ptr& monitor, + double timeout) : Device(path, hidpp::DefaultDevice, monitor, timeout) { +} + +void Receiver::_receiverCheck() { + // Check if the device is a receiver + try { + getNotificationFlags(); + } catch (hidpp10::Error& e) { + if (e.code() == Error::InvalidAddress) + throw InvalidReceiver(); + } + + // TODO: is there a better way of checking if this is a bolt receiver? + _is_bolt = pid() == 0xc548; +} + +Receiver::NotificationFlags Receiver::getNotificationFlags() { + auto response = getRegister(EnableHidppNotifications, {}, hidpp::ReportType::Short); + + NotificationFlags flags{}; + flags.deviceBatteryStatus = response[0] & (1 << 4); + flags.receiverWirelessNotifications = response[1] & (1 << 0); + flags.receiverSoftwarePresent = response[1] & (1 << 3); + + return flags; +} + +void Receiver::setNotifications(NotificationFlags flags) { + std::vector request(3); + + if (flags.deviceBatteryStatus) + request[0] |= (1 << 4); + if (flags.receiverWirelessNotifications) + request[1] |= 1; + if (flags.receiverSoftwarePresent) + request[1] |= (1 << 3); + + setRegister(EnableHidppNotifications, request, hidpp::ReportType::Short); +} + +void Receiver::enumerate() { + setRegisterNoResponse(ConnectionState, {2}, hidpp::ReportType::Short); +} + +///TODO: Investigate usage +uint8_t Receiver::getConnectionState(hidpp::DeviceIndex index) { + auto response = getRegister(ConnectionState, {index}, hidpp::ReportType::Short); + + return response[0]; +} + +void Receiver::startPairing(uint8_t timeout) { + std::vector request(3); + + if (_is_bolt) + throw std::invalid_argument("unifying pairing on bolt"); + + request[0] = 1; + request[1] = hidpp::DefaultDevice; + request[2] = timeout; + + if (_is_bolt) { + setRegister(BoltDevicePairing, request, hidpp::ReportType::Long); + } else { + setRegister(DevicePairing, request, hidpp::ReportType::Short); + } +} + +// bolt pairing request from solaar +void Receiver::startBoltPairing(const DeviceDiscoveryEvent& discovery) { + std::vector request(10); + + request[0] = 1; // start pair + request[1] = 0; // slot, from solaar. what does this mean? + + for(int i = 0; i < 6; ++i) + request[2 + i] = (discovery.address >> (i*8)) & 0xff; + + request[8] = discovery.authentication; + + // TODO: what does entropy do? + request[9] = (discovery.deviceType == hidpp::DeviceKeyboard) ? 10 : 20; + + setRegister(BoltDevicePairing, request, hidpp::ReportType::Long); +} + +void Receiver::stopPairing() { + std::vector request(3); + + request[0] = 2; + request[1] = hidpp::DefaultDevice; + + if (_is_bolt) + setRegister(BoltDevicePairing, request, hidpp::ReportType::Long); + else + setRegister(DevicePairing, request, hidpp::ReportType::Short); +} + +void Receiver::startDiscover(uint8_t timeout) { + std::vector request = {timeout, 1}; + + if (!_is_bolt) + throw std::invalid_argument("not a bolt receiver"); + + setRegister(BoltDeviceDiscovery, request, hidpp::ReportType::Short); +} + +void Receiver::stopDiscover() { + std::vector request = {0, 2}; + + if (!_is_bolt) + throw std::invalid_argument("not a bolt receiver"); + + setRegister(BoltDeviceDiscovery, request, hidpp::ReportType::Short); +} + +void Receiver::disconnect(hidpp::DeviceIndex index) { + std::vector request(2); + + request[0] = _is_bolt ? 3 : 2; + request[1] = index; + + if (_is_bolt) + setRegister(BoltDevicePairing, request, hidpp::ReportType::Long); + else + setRegister(DevicePairing, request, hidpp::ReportType::Short); +} + +std::map Receiver::getDeviceActivity() { + auto response = getRegister(DeviceActivity, {}, hidpp::ReportType::Long); + + std::map device_activity; + for (uint8_t i = hidpp::WirelessDevice1; i <= hidpp::WirelessDevice6; i++) + device_activity[static_cast(i)] = response[i]; + + return device_activity; +} + +struct Receiver::PairingInfo +Receiver::getPairingInfo(hidpp::DeviceIndex index) { + std::vector request(1); + request[0] = index; + if (_is_bolt) + request[0] += 0x50; + else + request[0] += 0x1f; + + auto response = getRegister(PairingInfo, request, hidpp::ReportType::Long); + + struct PairingInfo info{}; + if (_is_bolt) { + info = { + .destinationId = 0, // no longer given? + .reportInterval = 0, // no longer given? + .pid = (uint16_t) ((response[3] << 8) | response[2]), + .deviceType = static_cast(response[1]) + }; + } else { + info = { + .destinationId = response[1], + .reportInterval = response[2], + .pid = (uint16_t) ((response[3] << 8) | response[4]), + .deviceType = static_cast(response[7]) + }; + } + + + return info; +} + +struct Receiver::ExtendedPairingInfo +Receiver::getExtendedPairingInfo(hidpp::DeviceIndex index) { + const int device_num_offset = _is_bolt ? 0x50 : 0x2f; + const int serial_num_offset = _is_bolt ? 4 : 1; + const int report_offset = _is_bolt ? 8 : 5; + const int psl_offset = _is_bolt ? 12 : 8; + + std::vector request(1, index + device_num_offset); + + auto response = getRegister(PairingInfo, request, hidpp::ReportType::Long); + + ExtendedPairingInfo info{}; + + info.serialNumber = 0; + for (uint8_t i = 0; i < 4; i++) + info.serialNumber |= (response[i + serial_num_offset] << 8 * i); + + for (uint8_t i = 0; i < 4; i++) + info.reportTypes[i] = response[i + report_offset]; + + uint8_t psl = response[psl_offset] & 0xf; + if (psl > 0xc) + info.powerSwitchLocation = PowerSwitchLocation::Reserved; + else + info.powerSwitchLocation = static_cast(psl); + + return info; +} + +std::string Receiver::getDeviceName(hidpp::DeviceIndex index) { + std::vector request(2); + std::string name; + request[0] = index; + if (_is_bolt) { + /* Undocumented, deduced the following + * param 1 refers to part of string, 1-indexed + * + * response at 0x01 is [reg] [param 1] [size] [str...] + * response at 0x02-... is [next part of str...] + */ + request[0] += 0x60; + request[1] = 0x01; + + auto resp = getRegister(PairingInfo, request, hidpp::ReportType::Long); + const uint8_t size = resp[2]; + const uint8_t chunk_size = resp.size() - 3; + const uint8_t chunks = size / chunk_size + (size % chunk_size ? 1 : 0); + + name.resize(size, ' '); + for (int i = 0; i < chunks; ++i) { + for (int j = 0; j < chunk_size; ++j) { + name[i * chunk_size + j] = (char) resp[j + 3]; + } + + if (i < chunks - 1) { + request[1] = i + 1; + resp = getRegister(PairingInfo, request, hidpp::ReportType::Long); + } + } + + } else { + request[0] += 0x3f; + + auto response = getRegister(PairingInfo, request, hidpp::ReportType::Long); + + const uint8_t size = response[1]; + + name.resize(size, ' '); + for (std::size_t i = 0; i < size && i + 2 < response.size(); i++) + name[i] = (char) (response[i + 2]); + + } + + return name; +} + +hidpp::DeviceIndex Receiver::deviceDisconnectionEvent(const hidpp::Report& report) { + assert(report.subId() == DeviceDisconnection); + return report.deviceIndex(); +} + +hidpp::DeviceConnectionEvent Receiver::deviceConnectionEvent(const hidpp::Report& report) { + assert(report.subId() == DeviceConnection); + + auto data = report.paramBegin(); + + return { + .index = report.deviceIndex(), + .pid = (uint16_t) ((data[2] << 8) | data[1]), + .deviceType = static_cast(data[0] & 0x0f), + .unifying = ((report.address() & 0b111) == 0x04), + .softwarePresent = bool(data[0] & (1 << 4)), + .encrypted = (bool) (data[0] & (1 << 5)), + .linkEstablished = !(data[0] & (1 << 6)), + .withPayload = (bool) (data[0] & (1 << 7)), + + .fromTimeoutCheck = false, + }; +} + +bool Receiver::fillDeviceDiscoveryEvent(DeviceDiscoveryEvent& event, + const hidpp::Report& report) { + assert(report.subId() == DeviceDiscovered); + + auto data = report.paramBegin(); + + if (data[1] == 0) { + // device discovery event + + uint64_t address = 0 ; + for (int i = 0; i < 6; ++i) + address |= ((uint64_t)(data[6 + i]) << (8*i)); + + event.deviceType = static_cast(data[3]); + event.pid = (data[5] << 8) | data[4]; + event.address = address; + event.authentication = data[14]; + event.seq_num = report.address(); + event.name = ""; + + return false; + } else { + /* bad sequence, do not continue */ + if (event.seq_num != report.address()) + return false; + + const int block_size = hidpp::LongParamLength - 3; + + if (data[1] == 1) { + event.name.resize(data[2], ' '); + } + + for(int i = 0; i < block_size; ++i) { + const size_t j = (data[1]-1)*block_size + i; + if (j < event.name.size()) { + event.name[j] = (char)data[i + 3]; + } else { + return true; + } + } + + return false; + } +} + +PairStatusEvent Receiver::pairStatusEvent(const hidpp::Report& report) { + assert(report.subId() == PairStatus); + + return { + .pairing = (bool)(report.paramBegin()[0] & 1), + .error = static_cast(report.paramBegin()[1]) + }; +} + +BoltPairStatusEvent Receiver::boltPairStatusEvent(const hidpp::Report& report) { + assert(report.subId() == BoltPairStatus); + + return { + .pairing = report.address() == 0, + .error = report.paramBegin()[1] + }; +} + +DiscoveryStatusEvent Receiver::discoveryStatusEvent(const hidpp::Report& report) { + assert(report.subId() == DiscoveryStatus); + + return { + .discovering = report.address() == 0, + .error = report.paramBegin()[1] + }; +} + +std::string Receiver::passkeyEvent(const hidpp::Report& report) { + assert(report.subId() == PasskeyRequest); + + return {report.paramBegin(), report.paramBegin() + 6}; +} + +bool Receiver::bolt() const { + return _is_bolt; +} diff --git a/src/logid/backend/hidpp10/Receiver.h b/src/logid/backend/hidpp10/Receiver.h index 91a7038..6eee71b 100644 --- a/src/logid/backend/hidpp10/Receiver.h +++ b/src/logid/backend/hidpp10/Receiver.h @@ -1,221 +1,221 @@ -/* - * Copyright 2019-2023 PixlOne - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * 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 - -#include -#include - -namespace logid::backend::hidpp { - enum DeviceType : uint8_t { - DeviceUnknown = 0x00, - DeviceKeyboard = 0x01, - DeviceMouse = 0x02, - DeviceNumpad = 0x03, - DevicePresenter = 0x04, - /* 0x05-0x07 is reserved */ - DeviceTrackball = 0x08, - DeviceTouchpad = 0x09 - }; - - struct DeviceConnectionEvent { - DeviceIndex index{}; - uint16_t pid{}; - DeviceType deviceType = DeviceUnknown; - bool unifying{}; - bool softwarePresent{}; - bool encrypted{}; - bool linkEstablished{}; - bool withPayload{}; - bool fromTimeoutCheck = false; // Fake field - }; -} - -namespace logid::backend::hidpp10 { - - struct DeviceDiscoveryEvent { - hidpp::DeviceType deviceType = hidpp::DeviceUnknown; - uint8_t seq_num{}; - uint64_t address{}; - uint16_t pid{}; - uint8_t authentication{}; - std::string name; - }; - - enum PairingError : uint8_t { - NoPairingError = 0x00, - Timeout = 0x01, - UnsupportedDevice = 0x02, - TooManyDevices = 0x03, - /* Errors 0x04-0x05 are reserved */ - ConnectionSeqTimeout = 0x06, - }; - - struct PairStatusEvent { - bool pairing{}; - PairingError error = NoPairingError; - }; - - struct BoltPairStatusEvent { - bool pairing{}; - uint8_t error; - }; - - struct DiscoveryStatusEvent { - bool discovering{}; - uint8_t error{}; // don't know the error codes - }; - - class InvalidReceiver : public std::exception { - public: - [[nodiscard]] const char* what() const noexcept override; - }; - - class Receiver : public Device { - public: - - /* 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 Events : uint8_t { - // These events are identical to their DJ counterparts - DeviceDisconnection = 0x40, - DeviceConnection = 0x41, - PairStatus = 0x4a, - PasskeyRequest = 0x4d, - DeviceDiscovered = 0x4f, - DiscoveryStatus = 0x53, - BoltPairStatus = 0x54, - }; - - enum Registers : uint8_t { - EnableHidppNotifications = 0x00, - ConnectionState = 0x02, - DevicePairing = 0xb2, - DeviceActivity = 0xb3, - PairingInfo = 0xb5, - BoltDeviceDiscovery = 0xc0, - BoltDevicePairing = 0xc1, - }; - - struct NotificationFlags { - bool deviceBatteryStatus; - bool receiverWirelessNotifications; - bool receiverSoftwarePresent; - }; - - NotificationFlags getNotificationFlags(); - - void setNotifications(NotificationFlags flags); - - void enumerate(); - - uint8_t getConnectionState(hidpp::DeviceIndex index); - - void startPairing(uint8_t timeout = 0); - - void startBoltPairing(const DeviceDiscoveryEvent& discovery); - - void stopPairing(); - - void startDiscover(uint8_t timeout = 0); - - void stopDiscover(); - - void disconnect(hidpp::DeviceIndex index); - - std::map getDeviceActivity(); - - [[nodiscard]] bool bolt() const; - - struct PairingInfo { - uint8_t destinationId; - uint8_t reportInterval; - uint16_t pid; - hidpp::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]; - PowerSwitchLocation powerSwitchLocation; - }; - - struct PairingInfo getPairingInfo(hidpp::DeviceIndex index); - - struct ExtendedPairingInfo getExtendedPairingInfo(hidpp::DeviceIndex index); - - std::string getDeviceName(hidpp::DeviceIndex index); - - static hidpp::DeviceIndex deviceDisconnectionEvent(const hidpp::Report& report); - - static hidpp::DeviceConnectionEvent deviceConnectionEvent(const hidpp::Report& report); - - static PairStatusEvent pairStatusEvent(const hidpp::Report& report); - - static BoltPairStatusEvent boltPairStatusEvent(const hidpp::Report& report); - - static DiscoveryStatusEvent discoveryStatusEvent(const hidpp::Report& report); - - static bool fillDeviceDiscoveryEvent(DeviceDiscoveryEvent& event, - const hidpp::Report& report); - - static std::string passkeyEvent(const hidpp::Report& report); - - protected: - Receiver(const std::string& path, - const std::shared_ptr& monitor, - double timeout); - - private: - void _receiverCheck(); - - bool _is_bolt = false; - - public: - template - static std::shared_ptr make(Args... args) { - auto receiver = makeDerived(std::forward(args)...); - - receiver->_receiverCheck(); - - return receiver; - } - }; -} - +/* + * Copyright 2019-2023 PixlOne + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * 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 + +#include +#include + +namespace logid::backend::hidpp { + enum DeviceType : uint8_t { + DeviceUnknown = 0x00, + DeviceKeyboard = 0x01, + DeviceMouse = 0x02, + DeviceNumpad = 0x03, + DevicePresenter = 0x04, + /* 0x05-0x07 is reserved */ + DeviceTrackball = 0x08, + DeviceTouchpad = 0x09 + }; + + struct DeviceConnectionEvent { + DeviceIndex index{}; + uint16_t pid{}; + DeviceType deviceType = DeviceUnknown; + bool unifying{}; + bool softwarePresent{}; + bool encrypted{}; + bool linkEstablished{}; + bool withPayload{}; + bool fromTimeoutCheck = false; // Fake field + }; +} + +namespace logid::backend::hidpp10 { + + struct DeviceDiscoveryEvent { + hidpp::DeviceType deviceType = hidpp::DeviceUnknown; + uint8_t seq_num{}; + uint64_t address{}; + uint16_t pid{}; + uint8_t authentication{}; + std::string name; + }; + + enum PairingError : uint8_t { + NoPairingError = 0x00, + Timeout = 0x01, + UnsupportedDevice = 0x02, + TooManyDevices = 0x03, + /* Errors 0x04-0x05 are reserved */ + ConnectionSeqTimeout = 0x06, + }; + + struct PairStatusEvent { + bool pairing{}; + PairingError error = NoPairingError; + }; + + struct BoltPairStatusEvent { + bool pairing{}; + uint8_t error; + }; + + struct DiscoveryStatusEvent { + bool discovering{}; + uint8_t error{}; // don't know the error codes + }; + + class InvalidReceiver : public std::exception { + public: + [[nodiscard]] const char* what() const noexcept override; + }; + + class Receiver : public Device { + public: + + /* 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 Events : uint8_t { + // These events are identical to their DJ counterparts + DeviceDisconnection = 0x40, + DeviceConnection = 0x41, + PairStatus = 0x4a, + PasskeyRequest = 0x4d, + DeviceDiscovered = 0x4f, + DiscoveryStatus = 0x53, + BoltPairStatus = 0x54, + }; + + enum Registers : uint8_t { + EnableHidppNotifications = 0x00, + ConnectionState = 0x02, + DevicePairing = 0xb2, + DeviceActivity = 0xb3, + PairingInfo = 0xb5, + BoltDeviceDiscovery = 0xc0, + BoltDevicePairing = 0xc1, + }; + + struct NotificationFlags { + bool deviceBatteryStatus; + bool receiverWirelessNotifications; + bool receiverSoftwarePresent; + }; + + NotificationFlags getNotificationFlags(); + + void setNotifications(NotificationFlags flags); + + void enumerate(); + + uint8_t getConnectionState(hidpp::DeviceIndex index); + + void startPairing(uint8_t timeout = 0); + + void startBoltPairing(const DeviceDiscoveryEvent& discovery); + + void stopPairing(); + + void startDiscover(uint8_t timeout = 0); + + void stopDiscover(); + + void disconnect(hidpp::DeviceIndex index); + + std::map getDeviceActivity(); + + [[nodiscard]] bool bolt() const; + + struct PairingInfo { + uint8_t destinationId; + uint8_t reportInterval; + uint16_t pid; + hidpp::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]; + PowerSwitchLocation powerSwitchLocation; + }; + + struct PairingInfo getPairingInfo(hidpp::DeviceIndex index); + + struct ExtendedPairingInfo getExtendedPairingInfo(hidpp::DeviceIndex index); + + std::string getDeviceName(hidpp::DeviceIndex index); + + static hidpp::DeviceIndex deviceDisconnectionEvent(const hidpp::Report& report); + + static hidpp::DeviceConnectionEvent deviceConnectionEvent(const hidpp::Report& report); + + static PairStatusEvent pairStatusEvent(const hidpp::Report& report); + + static BoltPairStatusEvent boltPairStatusEvent(const hidpp::Report& report); + + static DiscoveryStatusEvent discoveryStatusEvent(const hidpp::Report& report); + + static bool fillDeviceDiscoveryEvent(DeviceDiscoveryEvent& event, + const hidpp::Report& report); + + static std::string passkeyEvent(const hidpp::Report& report); + + protected: + Receiver(const std::string& path, + const std::shared_ptr& monitor, + double timeout); + + private: + void _receiverCheck(); + + bool _is_bolt = false; + + public: + template + static std::shared_ptr make(Args... args) { + auto receiver = makeDerived(std::forward(args)...); + + receiver->_receiverCheck(); + + return receiver; + } + }; +} + #endif //LOGID_BACKEND_DJ_RECEIVER_H \ No newline at end of file diff --git a/src/logid/backend/hidpp10/ReceiverMonitor.cpp b/src/logid/backend/hidpp10/ReceiverMonitor.cpp index 026f296..e9fc74c 100644 --- a/src/logid/backend/hidpp10/ReceiverMonitor.cpp +++ b/src/logid/backend/hidpp10/ReceiverMonitor.cpp @@ -1,252 +1,252 @@ -/* - * Copyright 2019-2023 PixlOne - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * 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 - -using namespace logid::backend::hidpp10; -using namespace logid::backend::hidpp; - -ReceiverMonitor::ReceiverMonitor(const std::string& path, - const std::shared_ptr& monitor, double timeout) - : _receiver(Receiver::make(path, monitor, timeout)) { - - Receiver::NotificationFlags notification_flags{true, true, true}; - _receiver->setNotifications(notification_flags); -} - -void ReceiverMonitor::_ready() { - if (_connect_ev_handler.empty()) { - _connect_ev_handler = _receiver->rawDevice()->addEventHandler( - {[](const std::vector& report) -> bool { - if (report[Offset::Type] == Report::Type::Short || - report[Offset::Type] == Report::Type::Long) { - uint8_t sub_id = report[Offset::SubID]; - return (sub_id == Receiver::DeviceConnection || - sub_id == Receiver::DeviceDisconnection); - } - return false; - }, [self_weak = _self](const std::vector& raw) -> void { - /* Running in a new thread prevents deadlocks since the - * receiver may be enumerating. - */ - hidpp::Report report(raw); - - if (auto self = self_weak.lock()) { - run_task([self_weak, report]() { - auto self = self_weak.lock(); - if (!self) - return; - - if (report.subId() == Receiver::DeviceConnection) { - self->_addHandler(Receiver::deviceConnectionEvent(report)); - } else if (report.subId() == Receiver::DeviceDisconnection) { - self->_removeHandler(Receiver::deviceDisconnectionEvent(report)); - } - }); - } - - } - }); - } - - if (_discover_ev_handler.empty()) { - _discover_ev_handler = _receiver->addEventHandler( - {[](const hidpp::Report& report) -> bool { - return (report.subId() == Receiver::DeviceDiscovered) && - (report.type() == Report::Type::Long); - }, - [self_weak = _self](const hidpp::Report& report) { - auto self = self_weak.lock(); - if (!self) - return; - std::lock_guard lock(self->_pair_mutex); - if (self->_pair_state == Discovering) { - bool filled = Receiver::fillDeviceDiscoveryEvent( - self->_discovery_event, report); - - if (filled) { - self->_pair_state = FindingPasskey; - run_task([self_weak, event = self->_discovery_event]() { - if (auto self = self_weak.lock()) - self->receiver()->startBoltPairing(event); - }); - } - } - } - }); - } - - if (_passkey_ev_handler.empty()) { - _passkey_ev_handler = _receiver->addEventHandler( - {[](const hidpp::Report& report) -> bool { - return report.subId() == Receiver::PasskeyRequest && - report.type() == hidpp::Report::Type::Long; - }, - [self_weak = _self](const hidpp::Report& report) { - if (auto self = self_weak.lock()) { - std::lock_guard lock(self->_pair_mutex); - if (self->_pair_state == FindingPasskey) { - auto passkey = Receiver::passkeyEvent(report); - - self->_pair_state = Pairing; - self->pairReady(self->_discovery_event, passkey); - } - } - } - }); - } - - if (_pair_status_handler.empty()) { - _pair_status_handler = _receiver->addEventHandler( - {[](const hidpp::Report& report) -> bool { - return report.subId() == Receiver::DiscoveryStatus || - report.subId() == Receiver::PairStatus || - report.subId() == Receiver::BoltPairStatus; - }, - [self_weak = _self](const hidpp::Report& report) { - auto self = self_weak.lock(); - if (!self) - return; - - std::lock_guard lock(self->_pair_mutex); - // TODO: forward status to user - if (report.subId() == Receiver::DiscoveryStatus) { - auto event = Receiver::discoveryStatusEvent(report); - - if (self->_pair_state == Discovering && !event.discovering) - self->_pair_state = NotPairing; - } else if (report.subId() == Receiver::PairStatus) { - auto event = Receiver::pairStatusEvent(report); - - if ((self->_pair_state == FindingPasskey || - self->_pair_state == Pairing) && !event.pairing) - self->_pair_state = NotPairing; - } else if (report.subId() == Receiver::BoltPairStatus) { - auto event = Receiver::boltPairStatusEvent(report); - - if ((self->_pair_state == FindingPasskey || - self->_pair_state == Pairing) && !event.pairing) - self->_pair_state = NotPairing; - } - } - }); - } - - enumerate(); -} - -void ReceiverMonitor::enumerate() { - _receiver->enumerate(); -} - -void ReceiverMonitor::waitForDevice(hidpp::DeviceIndex index) { - const std::lock_guard lock(_wait_mutex); - if (!_waiters.count(index)) { - _waiters.emplace(index, _receiver->rawDevice()->addEventHandler( - {[index](const std::vector& report) -> bool { - /* Connection events should be handled by connect_ev_handler */ - auto sub_id = report[Offset::SubID]; - return report[Offset::DeviceIndex] == index && - sub_id != Receiver::DeviceConnection && - sub_id != Receiver::DeviceDisconnection; - }, - [self_weak = _self, index]( - [[maybe_unused]] const std::vector& report) { - hidpp::DeviceConnectionEvent event{}; - event.withPayload = false; - event.linkEstablished = true; - event.index = index; - event.fromTimeoutCheck = true; - - run_task([self_weak, event]() { - if (auto self = self_weak.lock()) - self->_addHandler(event); - }); - } - })); - } -} - -std::shared_ptr ReceiverMonitor::receiver() const { - return _receiver; -} - -void ReceiverMonitor::_startPair(uint8_t timeout) { - { - std::lock_guard lock(_pair_mutex); - _pair_state = _receiver->bolt() ? Discovering : Pairing; - _discovery_event = {}; - } - - if (_receiver->bolt()) - receiver()->startDiscover(timeout); - else - receiver()->startPairing(timeout); -} - -void ReceiverMonitor::_stopPair() { - PairState last_state; - { - std::lock_guard lock(_pair_mutex); - last_state = _pair_state; - _pair_state = NotPairing; - } - - if (last_state == Discovering) - receiver()->stopDiscover(); - else if (last_state == Pairing || last_state == FindingPasskey) - receiver()->stopPairing(); -} - -void ReceiverMonitor::_addHandler(const hidpp::DeviceConnectionEvent& event, int tries) { - auto device_path = _receiver->devicePath(); - try { - addDevice(event); - const std::lock_guard lock(_wait_mutex); - _waiters.erase(event.index); - } catch (DeviceNotReady& e) { - if (tries == max_tries) { - logPrintf(WARN, "Failed to add device %s:%d after %d tries." - "Treating as failure.", device_path.c_str(), event.index, max_tries); - } else { - /* Do exponential backoff for 2^tries * backoff ms. */ - std::chrono::milliseconds wait((1 << tries) * ready_backoff); - logPrintf(DEBUG, "Failed to add device %s:%d on try %d, backing off for %dms", - device_path.c_str(), event.index, tries + 1, wait.count()); - run_task_after([self_weak = _self, event, tries]() { - if (auto self = self_weak.lock()) - self->_addHandler(event, tries + 1); - }, wait); - } - } catch (std::exception& e) { - logPrintf(ERROR, "Failed to add device %d to receiver on %s: %s", - event.index, device_path.c_str(), e.what()); - } -} - -void ReceiverMonitor::_removeHandler(hidpp::DeviceIndex index) { - try { - removeDevice(index); - } catch (std::exception& e) { - logPrintf(ERROR, "Failed to remove device %d from receiver on %s: %s", - index, _receiver->devicePath().c_str(), e.what()); - } -} +/* + * Copyright 2019-2023 PixlOne + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * 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 + +using namespace logid::backend::hidpp10; +using namespace logid::backend::hidpp; + +ReceiverMonitor::ReceiverMonitor(const std::string& path, + const std::shared_ptr& monitor, double timeout) + : _receiver(Receiver::make(path, monitor, timeout)) { + + Receiver::NotificationFlags notification_flags{true, true, true}; + _receiver->setNotifications(notification_flags); +} + +void ReceiverMonitor::_ready() { + if (_connect_ev_handler.empty()) { + _connect_ev_handler = _receiver->rawDevice()->addEventHandler( + {[](const std::vector& report) -> bool { + if (report[Offset::Type] == Report::Type::Short || + report[Offset::Type] == Report::Type::Long) { + uint8_t sub_id = report[Offset::SubID]; + return (sub_id == Receiver::DeviceConnection || + sub_id == Receiver::DeviceDisconnection); + } + return false; + }, [self_weak = _self](const std::vector& raw) -> void { + /* Running in a new thread prevents deadlocks since the + * receiver may be enumerating. + */ + hidpp::Report report(raw); + + if (auto self = self_weak.lock()) { + run_task([self_weak, report]() { + auto self = self_weak.lock(); + if (!self) + return; + + if (report.subId() == Receiver::DeviceConnection) { + self->_addHandler(Receiver::deviceConnectionEvent(report)); + } else if (report.subId() == Receiver::DeviceDisconnection) { + self->_removeHandler(Receiver::deviceDisconnectionEvent(report)); + } + }); + } + + } + }); + } + + if (_discover_ev_handler.empty()) { + _discover_ev_handler = _receiver->addEventHandler( + {[](const hidpp::Report& report) -> bool { + return (report.subId() == Receiver::DeviceDiscovered) && + (report.type() == Report::Type::Long); + }, + [self_weak = _self](const hidpp::Report& report) { + auto self = self_weak.lock(); + if (!self) + return; + std::lock_guard lock(self->_pair_mutex); + if (self->_pair_state == Discovering) { + bool filled = Receiver::fillDeviceDiscoveryEvent( + self->_discovery_event, report); + + if (filled) { + self->_pair_state = FindingPasskey; + run_task([self_weak, event = self->_discovery_event]() { + if (auto self = self_weak.lock()) + self->receiver()->startBoltPairing(event); + }); + } + } + } + }); + } + + if (_passkey_ev_handler.empty()) { + _passkey_ev_handler = _receiver->addEventHandler( + {[](const hidpp::Report& report) -> bool { + return report.subId() == Receiver::PasskeyRequest && + report.type() == hidpp::Report::Type::Long; + }, + [self_weak = _self](const hidpp::Report& report) { + if (auto self = self_weak.lock()) { + std::lock_guard lock(self->_pair_mutex); + if (self->_pair_state == FindingPasskey) { + auto passkey = Receiver::passkeyEvent(report); + + self->_pair_state = Pairing; + self->pairReady(self->_discovery_event, passkey); + } + } + } + }); + } + + if (_pair_status_handler.empty()) { + _pair_status_handler = _receiver->addEventHandler( + {[](const hidpp::Report& report) -> bool { + return report.subId() == Receiver::DiscoveryStatus || + report.subId() == Receiver::PairStatus || + report.subId() == Receiver::BoltPairStatus; + }, + [self_weak = _self](const hidpp::Report& report) { + auto self = self_weak.lock(); + if (!self) + return; + + std::lock_guard lock(self->_pair_mutex); + // TODO: forward status to user + if (report.subId() == Receiver::DiscoveryStatus) { + auto event = Receiver::discoveryStatusEvent(report); + + if (self->_pair_state == Discovering && !event.discovering) + self->_pair_state = NotPairing; + } else if (report.subId() == Receiver::PairStatus) { + auto event = Receiver::pairStatusEvent(report); + + if ((self->_pair_state == FindingPasskey || + self->_pair_state == Pairing) && !event.pairing) + self->_pair_state = NotPairing; + } else if (report.subId() == Receiver::BoltPairStatus) { + auto event = Receiver::boltPairStatusEvent(report); + + if ((self->_pair_state == FindingPasskey || + self->_pair_state == Pairing) && !event.pairing) + self->_pair_state = NotPairing; + } + } + }); + } + + enumerate(); +} + +void ReceiverMonitor::enumerate() { + _receiver->enumerate(); +} + +void ReceiverMonitor::waitForDevice(hidpp::DeviceIndex index) { + const std::lock_guard lock(_wait_mutex); + if (!_waiters.count(index)) { + _waiters.emplace(index, _receiver->rawDevice()->addEventHandler( + {[index](const std::vector& report) -> bool { + /* Connection events should be handled by connect_ev_handler */ + auto sub_id = report[Offset::SubID]; + return report[Offset::DeviceIndex] == index && + sub_id != Receiver::DeviceConnection && + sub_id != Receiver::DeviceDisconnection; + }, + [self_weak = _self, index]( + [[maybe_unused]] const std::vector& report) { + hidpp::DeviceConnectionEvent event{}; + event.withPayload = false; + event.linkEstablished = true; + event.index = index; + event.fromTimeoutCheck = true; + + run_task([self_weak, event]() { + if (auto self = self_weak.lock()) + self->_addHandler(event); + }); + } + })); + } +} + +std::shared_ptr ReceiverMonitor::receiver() const { + return _receiver; +} + +void ReceiverMonitor::_startPair(uint8_t timeout) { + { + std::lock_guard lock(_pair_mutex); + _pair_state = _receiver->bolt() ? Discovering : Pairing; + _discovery_event = {}; + } + + if (_receiver->bolt()) + receiver()->startDiscover(timeout); + else + receiver()->startPairing(timeout); +} + +void ReceiverMonitor::_stopPair() { + PairState last_state; + { + std::lock_guard lock(_pair_mutex); + last_state = _pair_state; + _pair_state = NotPairing; + } + + if (last_state == Discovering) + receiver()->stopDiscover(); + else if (last_state == Pairing || last_state == FindingPasskey) + receiver()->stopPairing(); +} + +void ReceiverMonitor::_addHandler(const hidpp::DeviceConnectionEvent& event, int tries) { + auto device_path = _receiver->devicePath(); + try { + addDevice(event); + const std::lock_guard lock(_wait_mutex); + _waiters.erase(event.index); + } catch (DeviceNotReady& e) { + if (tries == max_tries) { + logPrintf(WARN, "Failed to add device %s:%d after %d tries." + "Treating as failure.", device_path.c_str(), event.index, max_tries); + } else { + /* Do exponential backoff for 2^tries * backoff ms. */ + std::chrono::milliseconds wait((1 << tries) * ready_backoff); + logPrintf(DEBUG, "Failed to add device %s:%d on try %d, backing off for %dms", + device_path.c_str(), event.index, tries + 1, wait.count()); + run_task_after([self_weak = _self, event, tries]() { + if (auto self = self_weak.lock()) + self->_addHandler(event, tries + 1); + }, wait); + } + } catch (std::exception& e) { + logPrintf(ERROR, "Failed to add device %d to receiver on %s: %s", + event.index, device_path.c_str(), e.what()); + } +} + +void ReceiverMonitor::_removeHandler(hidpp::DeviceIndex index) { + try { + removeDevice(index); + } catch (std::exception& e) { + logPrintf(ERROR, "Failed to remove device %d from receiver on %s: %s", + index, _receiver->devicePath().c_str(), e.what()); + } +} diff --git a/src/logid/backend/hidpp10/ReceiverMonitor.h b/src/logid/backend/hidpp10/ReceiverMonitor.h index 095fa25..efdee2b 100644 --- a/src/logid/backend/hidpp10/ReceiverMonitor.h +++ b/src/logid/backend/hidpp10/ReceiverMonitor.h @@ -1,119 +1,119 @@ -/* - * Copyright 2019-2023 PixlOne - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * 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 - -#include -#include -#include -#include - -namespace logid::backend::hidpp10 { - - template - class _receiverMonitorWrapper : public T { - friend class ReceiverMonitor; - - public: - template - explicit _receiverMonitorWrapper(Args... args) : T(std::forward(args)...) {} - - template - static std::shared_ptr make(Args... args) { - return std::make_shared<_receiverMonitorWrapper>(std::forward(args)...); - } - }; - - static constexpr int max_tries = 5; - static constexpr int ready_backoff = 250; - - // This class will run on the RawDevice thread, - class ReceiverMonitor { - public: - void enumerate(); - - ReceiverMonitor(const ReceiverMonitor&) = delete; - - ReceiverMonitor(ReceiverMonitor&&) = delete; - - protected: - ReceiverMonitor(const std::string& path, - const std::shared_ptr& monitor, - double timeout); - - - virtual void addDevice(hidpp::DeviceConnectionEvent event) = 0; - - virtual void removeDevice(hidpp::DeviceIndex index) = 0; - - virtual void pairReady(const hidpp10::DeviceDiscoveryEvent& event, - const std::string& passkey) = 0; - - void _startPair(uint8_t timeout = 0); - - void _stopPair(); - - void waitForDevice(hidpp::DeviceIndex index); - - [[nodiscard]] std::shared_ptr receiver() const; - - private: - void _ready(); - - void _addHandler(const hidpp::DeviceConnectionEvent& event, int tries = 0); - - void _removeHandler(hidpp::DeviceIndex index); - - std::shared_ptr _receiver; - - enum PairState { - NotPairing, - Discovering, - FindingPasskey, - Pairing, - }; - - std::mutex _pair_mutex; - DeviceDiscoveryEvent _discovery_event; - PairState _pair_state = NotPairing; - - EventHandlerLock _connect_ev_handler; - - EventHandlerLock _discover_ev_handler; - EventHandlerLock _passkey_ev_handler; - EventHandlerLock _pair_status_handler; - - std::weak_ptr _self; - - std::mutex _wait_mutex; - std::map> _waiters; - - public: - template - static std::shared_ptr make(Args... args) { - auto receiver_monitor = _receiverMonitorWrapper::make(std::forward(args)...); - receiver_monitor->_self = receiver_monitor; - receiver_monitor->_ready(); - return receiver_monitor; - } - }; - -} - +/* + * Copyright 2019-2023 PixlOne + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * 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 + +#include +#include +#include +#include + +namespace logid::backend::hidpp10 { + + template + class _receiverMonitorWrapper : public T { + friend class ReceiverMonitor; + + public: + template + explicit _receiverMonitorWrapper(Args... args) : T(std::forward(args)...) {} + + template + static std::shared_ptr make(Args... args) { + return std::make_shared<_receiverMonitorWrapper>(std::forward(args)...); + } + }; + + static constexpr int max_tries = 5; + static constexpr int ready_backoff = 250; + + // This class will run on the RawDevice thread, + class ReceiverMonitor { + public: + void enumerate(); + + ReceiverMonitor(const ReceiverMonitor&) = delete; + + ReceiverMonitor(ReceiverMonitor&&) = delete; + + protected: + ReceiverMonitor(const std::string& path, + const std::shared_ptr& monitor, + double timeout); + + + virtual void addDevice(hidpp::DeviceConnectionEvent event) = 0; + + virtual void removeDevice(hidpp::DeviceIndex index) = 0; + + virtual void pairReady(const hidpp10::DeviceDiscoveryEvent& event, + const std::string& passkey) = 0; + + void _startPair(uint8_t timeout = 0); + + void _stopPair(); + + void waitForDevice(hidpp::DeviceIndex index); + + [[nodiscard]] std::shared_ptr receiver() const; + + private: + void _ready(); + + void _addHandler(const hidpp::DeviceConnectionEvent& event, int tries = 0); + + void _removeHandler(hidpp::DeviceIndex index); + + std::shared_ptr _receiver; + + enum PairState { + NotPairing, + Discovering, + FindingPasskey, + Pairing, + }; + + std::mutex _pair_mutex; + DeviceDiscoveryEvent _discovery_event; + PairState _pair_state = NotPairing; + + EventHandlerLock _connect_ev_handler; + + EventHandlerLock _discover_ev_handler; + EventHandlerLock _passkey_ev_handler; + EventHandlerLock _pair_status_handler; + + std::weak_ptr _self; + + std::mutex _wait_mutex; + std::map> _waiters; + + public: + template + static std::shared_ptr make(Args... args) { + auto receiver_monitor = _receiverMonitorWrapper::make(std::forward(args)...); + receiver_monitor->_self = receiver_monitor; + receiver_monitor->_ready(); + return receiver_monitor; + } + }; + +} + #endif //LOGID_BACKEND_DJ_RECEIVERMONITOR_H \ No newline at end of file diff --git a/src/logid/backend/hidpp10/defs.h b/src/logid/backend/hidpp10/defs.h index ff18ed4..e012872 100644 --- a/src/logid/backend/hidpp10/defs.h +++ b/src/logid/backend/hidpp10/defs.h @@ -1,33 +1,33 @@ -/* - * Copyright 2019-2023 PixlOne - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * 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 - -namespace logid::backend::hidpp10 { - enum SubID : uint8_t { - SetRegisterShort = 0x80, - GetRegisterShort = 0x81, - SetRegisterLong = 0x82, - GetRegisterLong = 0x83, - }; - - static constexpr size_t SubIDCount = 4; -} - +/* + * Copyright 2019-2023 PixlOne + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * 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 + +namespace logid::backend::hidpp10 { + enum SubID : uint8_t { + SetRegisterShort = 0x80, + GetRegisterShort = 0x81, + SetRegisterLong = 0x82, + GetRegisterLong = 0x83, + }; + + static constexpr size_t SubIDCount = 4; +} + #endif //LOGID_BACKEND_HIDPP10_DEFS_H \ No newline at end of file diff --git a/src/logid/backend/hidpp20/Device.cpp b/src/logid/backend/hidpp20/Device.cpp index adbcc27..eb563aa 100644 --- a/src/logid/backend/hidpp20/Device.cpp +++ b/src/logid/backend/hidpp20/Device.cpp @@ -1,162 +1,162 @@ -/* - * Copyright 2019-2023 PixlOne - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * 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 - -using namespace logid::backend; -using namespace logid::backend::hidpp20; - -Device::Device(const std::string& path, hidpp::DeviceIndex index, - const std::shared_ptr& monitor, double timeout) : - hidpp::Device(path, index, monitor, timeout) { -} - -Device::Device(std::shared_ptr raw_device, - hidpp::DeviceIndex index, double timeout) : - hidpp::Device(std::move(raw_device), index, timeout) { -} - -Device::Device(const std::shared_ptr& receiver, - hidpp::DeviceConnectionEvent event, double timeout) : - hidpp::Device(receiver, event, timeout) { -} - -Device::Device(const std::shared_ptr& receiver, - hidpp::DeviceIndex index, double timeout) - : hidpp::Device(receiver, index, timeout) { -} - -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; - else - throw hidpp::Report::InvalidReportID(); - - hidpp::Report request(type, deviceIndex(), feature_index, function, - hidpp::softwareID); - std::copy(params.begin(), params.end(), request.paramBegin()); - - auto response = this->sendReport(request); - return {response.paramBegin(), response.paramEnd()}; -} - -void Device::callFunctionNoResponse(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; - else - throw hidpp::Report::InvalidReportID(); - - hidpp::Report request(type, deviceIndex(), feature_index, function, hidpp::softwareID); - std::copy(params.begin(), params.end(), request.paramBegin()); - - this->sendReportNoACK(request); -} - -hidpp::Report Device::sendReport(const hidpp::Report& report) { - auto& response_slot = _responses[report.feature() % _responses.size()]; - - std::unique_lock response_lock(_response_mutex); - _response_cv.wait(response_lock, [&response_slot]() { - return !response_slot.feature.has_value(); - }); - - response_slot.feature = report.feature(); - - _sendReport(report); - - bool valid = _response_cv.wait_for( - response_lock, io_timeout, - [&response_slot]() { - return response_slot.response.has_value(); - }); - - if (!valid) { - response_slot.reset(); - throw TimeoutError(); - } - - assert(response_slot.response.has_value()); - auto response = response_slot.response.value(); - response_slot.reset(); - - if (std::holds_alternative(response)) { - return std::get(response); - } else { // if(std::holds_alternative(response)) - auto error = std::get(response); - throw Error(error.error_code, error.device_index); - } -} - -void Device::sendReportNoACK(const hidpp::Report& report) { - hidpp::Report no_ack_report(report); - no_ack_report.setSwId(hidpp::noAckSoftwareID); - _sendReport(std::move(no_ack_report)); -} - -bool Device::responseReport(const hidpp::Report& report) { - auto& response_slot = _responses[report.feature() % _responses.size()]; - std::lock_guard lock(_response_mutex); - uint8_t sw_id, feature; - - bool is_error = false; - hidpp::Report::Hidpp20Error hidpp20_error{}; - if (report.isError20(hidpp20_error)) { - is_error = true; - sw_id = hidpp20_error.software_id; - feature = hidpp20_error.feature_index; - } else { - sw_id = report.swId(); - feature = report.feature(); - } - - if (sw_id != hidpp::softwareID) - return false; - - if (!response_slot.feature || response_slot.feature.value() != feature) { - return false; - } - - if (is_error) { - response_slot.response = hidpp20_error; - } else { - response_slot.response = report; - } - - _response_cv.notify_all(); - return true; -} - -void Device::ResponseSlot::reset() { - response.reset(); - feature.reset(); -} +/* + * Copyright 2019-2023 PixlOne + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * 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 + +using namespace logid::backend; +using namespace logid::backend::hidpp20; + +Device::Device(const std::string& path, hidpp::DeviceIndex index, + const std::shared_ptr& monitor, double timeout) : + hidpp::Device(path, index, monitor, timeout) { +} + +Device::Device(std::shared_ptr raw_device, + hidpp::DeviceIndex index, double timeout) : + hidpp::Device(std::move(raw_device), index, timeout) { +} + +Device::Device(const std::shared_ptr& receiver, + hidpp::DeviceConnectionEvent event, double timeout) : + hidpp::Device(receiver, event, timeout) { +} + +Device::Device(const std::shared_ptr& receiver, + hidpp::DeviceIndex index, double timeout) + : hidpp::Device(receiver, index, timeout) { +} + +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; + else + throw hidpp::Report::InvalidReportID(); + + hidpp::Report request(type, deviceIndex(), feature_index, function, + hidpp::softwareID); + std::copy(params.begin(), params.end(), request.paramBegin()); + + auto response = this->sendReport(request); + return {response.paramBegin(), response.paramEnd()}; +} + +void Device::callFunctionNoResponse(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; + else + throw hidpp::Report::InvalidReportID(); + + hidpp::Report request(type, deviceIndex(), feature_index, function, hidpp::softwareID); + std::copy(params.begin(), params.end(), request.paramBegin()); + + this->sendReportNoACK(request); +} + +hidpp::Report Device::sendReport(const hidpp::Report& report) { + auto& response_slot = _responses[report.feature() % _responses.size()]; + + std::unique_lock response_lock(_response_mutex); + _response_cv.wait(response_lock, [&response_slot]() { + return !response_slot.feature.has_value(); + }); + + response_slot.feature = report.feature(); + + _sendReport(report); + + bool valid = _response_cv.wait_for( + response_lock, io_timeout, + [&response_slot]() { + return response_slot.response.has_value(); + }); + + if (!valid) { + response_slot.reset(); + throw TimeoutError(); + } + + assert(response_slot.response.has_value()); + auto response = response_slot.response.value(); + response_slot.reset(); + + if (std::holds_alternative(response)) { + return std::get(response); + } else { // if(std::holds_alternative(response)) + auto error = std::get(response); + throw Error(error.error_code, error.device_index); + } +} + +void Device::sendReportNoACK(const hidpp::Report& report) { + hidpp::Report no_ack_report(report); + no_ack_report.setSwId(hidpp::noAckSoftwareID); + _sendReport(std::move(no_ack_report)); +} + +bool Device::responseReport(const hidpp::Report& report) { + auto& response_slot = _responses[report.feature() % _responses.size()]; + std::lock_guard lock(_response_mutex); + uint8_t sw_id, feature; + + bool is_error = false; + hidpp::Report::Hidpp20Error hidpp20_error{}; + if (report.isError20(hidpp20_error)) { + is_error = true; + sw_id = hidpp20_error.software_id; + feature = hidpp20_error.feature_index; + } else { + sw_id = report.swId(); + feature = report.feature(); + } + + if (sw_id != hidpp::softwareID) + return false; + + if (!response_slot.feature || response_slot.feature.value() != feature) { + return false; + } + + if (is_error) { + response_slot.response = hidpp20_error; + } else { + response_slot.response = report; + } + + _response_cv.notify_all(); + return true; +} + +void Device::ResponseSlot::reset() { + response.reset(); + feature.reset(); +} diff --git a/src/logid/backend/hidpp20/Device.h b/src/logid/backend/hidpp20/Device.h index a3e3103..df256f7 100644 --- a/src/logid/backend/hidpp20/Device.h +++ b/src/logid/backend/hidpp20/Device.h @@ -1,82 +1,82 @@ -/* - * Copyright 2019-2023 PixlOne - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * 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_DEVICE_H -#define LOGID_BACKEND_HIDPP20_DEVICE_H - -#include -#include -#include -#include -#include - -namespace logid::backend::hidpp20 { - class Device : public hidpp::Device { - public: - std::vector callFunction(uint8_t feature_index, - uint8_t function, - std::vector& params); - - void callFunctionNoResponse(uint8_t feature_index, - uint8_t function, - std::vector& params); - - hidpp::Report sendReport(const hidpp::Report& report) final; - - void sendReportNoACK(const hidpp::Report& report) final; - - protected: - Device(const std::string& path, hidpp::DeviceIndex index, - const std::shared_ptr& monitor, double timeout); - - Device(std::shared_ptr raw_device, - hidpp::DeviceIndex index, double timeout); - - Device(const std::shared_ptr& receiver, - hidpp::DeviceConnectionEvent event, double timeout); - - Device(const std::shared_ptr& receiver, - hidpp::DeviceIndex index, double timeout); - - bool responseReport(const hidpp::Report& report) final; - - private: - typedef std::variant Response; - struct ResponseSlot { - std::optional response; - std::optional feature; - void reset(); - }; - - /* Multiplex responses on lower nibble of SubID, ignore upper nibble for space */ - std::array _responses; - - public: - template - static std::shared_ptr make(Args... args) { - auto device = makeDerived(std::forward(args)...); - - if (std::get<0>(device->version()) < 2) - throw std::invalid_argument("not a hid++ 2.0 device"); - - return device; - } - }; -} - +/* + * Copyright 2019-2023 PixlOne + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * 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_DEVICE_H +#define LOGID_BACKEND_HIDPP20_DEVICE_H + +#include +#include +#include +#include +#include + +namespace logid::backend::hidpp20 { + class Device : public hidpp::Device { + public: + std::vector callFunction(uint8_t feature_index, + uint8_t function, + std::vector& params); + + void callFunctionNoResponse(uint8_t feature_index, + uint8_t function, + std::vector& params); + + hidpp::Report sendReport(const hidpp::Report& report) final; + + void sendReportNoACK(const hidpp::Report& report) final; + + protected: + Device(const std::string& path, hidpp::DeviceIndex index, + const std::shared_ptr& monitor, double timeout); + + Device(std::shared_ptr raw_device, + hidpp::DeviceIndex index, double timeout); + + Device(const std::shared_ptr& receiver, + hidpp::DeviceConnectionEvent event, double timeout); + + Device(const std::shared_ptr& receiver, + hidpp::DeviceIndex index, double timeout); + + bool responseReport(const hidpp::Report& report) final; + + private: + typedef std::variant Response; + struct ResponseSlot { + std::optional response; + std::optional feature; + void reset(); + }; + + /* Multiplex responses on lower nibble of SubID, ignore upper nibble for space */ + std::array _responses; + + public: + template + static std::shared_ptr make(Args... args) { + auto device = makeDerived(std::forward(args)...); + + if (std::get<0>(device->version()) < 2) + throw std::invalid_argument("not a hid++ 2.0 device"); + + return device; + } + }; +} + #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 4eb0772..172ed94 100644 --- a/src/logid/backend/hidpp20/Error.cpp +++ b/src/logid/backend/hidpp20/Error.cpp @@ -1,64 +1,64 @@ -/* - * Copyright 2019-2023 PixlOne - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * 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 - -using namespace logid::backend; -using namespace logid::backend::hidpp20; - -Error::Error(uint8_t code, hidpp::DeviceIndex index) : _code(code), _index (index) { - 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 "Unknown error code"; - } -} - -uint8_t Error::code() const noexcept { - return _code; -} - -hidpp::DeviceIndex Error::deviceIndex() const noexcept { - return _index; +/* + * Copyright 2019-2023 PixlOne + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * 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 + +using namespace logid::backend; +using namespace logid::backend::hidpp20; + +Error::Error(uint8_t code, hidpp::DeviceIndex index) : _code(code), _index (index) { + 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 "Unknown error code"; + } +} + +uint8_t Error::code() const noexcept { + return _code; +} + +hidpp::DeviceIndex Error::deviceIndex() const noexcept { + return _index; } \ No newline at end of file diff --git a/src/logid/backend/hidpp20/Error.h b/src/logid/backend/hidpp20/Error.h index 94e48eb..e98bea0 100644 --- a/src/logid/backend/hidpp20/Error.h +++ b/src/logid/backend/hidpp20/Error.h @@ -1,59 +1,59 @@ -/* - * Copyright 2019-2023 PixlOne - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * 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 - -#include -#include -#include - -namespace logid::backend::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, hidpp::DeviceIndex index); - - [[nodiscard]] const char* what() const noexcept override; - - [[nodiscard]] uint8_t code() const noexcept; - - [[nodiscard]] hidpp::DeviceIndex deviceIndex() const noexcept; - - private: - uint8_t _code; - hidpp::DeviceIndex _index; - }; -} - +/* + * Copyright 2019-2023 PixlOne + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * 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 + +#include +#include +#include + +namespace logid::backend::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, hidpp::DeviceIndex index); + + [[nodiscard]] const char* what() const noexcept override; + + [[nodiscard]] uint8_t code() const noexcept; + + [[nodiscard]] hidpp::DeviceIndex deviceIndex() const noexcept; + + private: + uint8_t _code; + hidpp::DeviceIndex _index; + }; +} + #endif //LOGID_BACKEND_HIDPP20_ERROR_H \ No newline at end of file diff --git a/src/logid/backend/hidpp20/EssentialFeature.cpp b/src/logid/backend/hidpp20/EssentialFeature.cpp index 1d836bf..f02ed1e 100644 --- a/src/logid/backend/hidpp20/EssentialFeature.cpp +++ b/src/logid/backend/hidpp20/EssentialFeature.cpp @@ -1,66 +1,66 @@ -/* - * Copyright 2019-2023 PixlOne - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * 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 - -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; - else - throw hidpp::Report::InvalidReportID(); - - hidpp::Report request(type, _device->deviceIndex(), _index, function_id, hidpp::softwareID); - std::copy(params.begin(), params.end(), request.paramBegin()); - - auto response = _device->sendReport(request); - return {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; - try { - _index = this->callFunction(Root::GetFeature, getFunc_req).at(0); - } catch (Error& e) { - if (e.code() == Error::InvalidFeatureIndex) - throw UnsupportedFeature(_id); - throw e; - } - - // 0 if not found - if (!_index) - throw UnsupportedFeature(_id); - } -} +/* + * Copyright 2019-2023 PixlOne + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * 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 + +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; + else + throw hidpp::Report::InvalidReportID(); + + hidpp::Report request(type, _device->deviceIndex(), _index, function_id, hidpp::softwareID); + std::copy(params.begin(), params.end(), request.paramBegin()); + + auto response = _device->sendReport(request); + return {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; + try { + _index = this->callFunction(Root::GetFeature, getFunc_req).at(0); + } catch (Error& e) { + if (e.code() == Error::InvalidFeatureIndex) + throw UnsupportedFeature(_id); + throw e; + } + + // 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 index 7e3f995..4f8c018 100644 --- a/src/logid/backend/hidpp20/EssentialFeature.h +++ b/src/logid/backend/hidpp20/EssentialFeature.h @@ -1,48 +1,48 @@ -/* - * Copyright 2019-2023 PixlOne - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * 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_ESSENTIAL_FEATURE_H -#define LOGID_BACKEND_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 - -namespace logid::backend::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); - - hidpp::Device* const _device; - uint8_t _index; - }; -} - +/* + * Copyright 2019-2023 PixlOne + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * 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_ESSENTIAL_FEATURE_H +#define LOGID_BACKEND_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 + +namespace logid::backend::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); + + hidpp::Device* const _device; + uint8_t _index; + }; +} + #endif //LOGID_BACKEND_HIDPP20_ESSENTIAL_FEATURE_H \ No newline at end of file diff --git a/src/logid/backend/hidpp20/Feature.cpp b/src/logid/backend/hidpp20/Feature.cpp index a810c6d..5ff1bd5 100644 --- a/src/logid/backend/hidpp20/Feature.cpp +++ b/src/logid/backend/hidpp20/Feature.cpp @@ -1,68 +1,68 @@ -/* - * Copyright 2019-2023 PixlOne - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * 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 - -using namespace logid::backend::hidpp20; - -const char* UnsupportedFeature::what() const noexcept { - return "Unsupported feature"; -} - -uint16_t 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); -} - -void Feature::callFunctionNoResponse(uint8_t function_id, - std::vector& params) { - _device->callFunctionNoResponse(_index, function_id, params); -} - -Feature::Feature(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; - - 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) - throw UnsupportedFeature(_id); - } -} - -uint8_t Feature::featureIndex() const { - return _index; +/* + * Copyright 2019-2023 PixlOne + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * 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 + +using namespace logid::backend::hidpp20; + +const char* UnsupportedFeature::what() const noexcept { + return "Unsupported feature"; +} + +uint16_t 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); +} + +void Feature::callFunctionNoResponse(uint8_t function_id, + std::vector& params) { + _device->callFunctionNoResponse(_index, function_id, params); +} + +Feature::Feature(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; + + 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) + throw UnsupportedFeature(_id); + } +} + +uint8_t Feature::featureIndex() const { + 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 1e269e2..0484297 100644 --- a/src/logid/backend/hidpp20/Feature.h +++ b/src/logid/backend/hidpp20/Feature.h @@ -1,63 +1,63 @@ -/* - * Copyright 2019-2023 PixlOne - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * 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_H -#define LOGID_BACKEND_HIDPP20_FEATURE_H - -#include -#include -#include - -namespace logid::backend::hidpp20 { - class Device; - - class UnsupportedFeature : public std::exception { - public: - explicit UnsupportedFeature(uint16_t ID) : _f_id(ID) {} - - [[nodiscard]] const char* what() const noexcept override; - - [[nodiscard]] uint16_t code() const noexcept; - - private: - uint16_t _f_id; - }; - - class Feature { - public: - static const uint16_t ID; - - virtual uint16_t getID() = 0; - - [[nodiscard]] uint8_t featureIndex() const; - - virtual ~Feature() = default; - - protected: - explicit Feature(Device* dev, uint16_t _id); - - std::vector callFunction(uint8_t function_id, std::vector& params); - - void callFunctionNoResponse(uint8_t function_id, std::vector& params); - - Device* const _device; - uint8_t _index; - }; -} - +/* + * Copyright 2019-2023 PixlOne + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * 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_H +#define LOGID_BACKEND_HIDPP20_FEATURE_H + +#include +#include +#include + +namespace logid::backend::hidpp20 { + class Device; + + class UnsupportedFeature : public std::exception { + public: + explicit UnsupportedFeature(uint16_t ID) : _f_id(ID) {} + + [[nodiscard]] const char* what() const noexcept override; + + [[nodiscard]] uint16_t code() const noexcept; + + private: + uint16_t _f_id; + }; + + class Feature { + public: + static const uint16_t ID; + + virtual uint16_t getID() = 0; + + [[nodiscard]] uint8_t featureIndex() const; + + virtual ~Feature() = default; + + protected: + explicit Feature(Device* dev, uint16_t _id); + + std::vector callFunction(uint8_t function_id, std::vector& params); + + void callFunctionNoResponse(uint8_t function_id, std::vector& params); + + Device* const _device; + uint8_t _index; + }; +} + #endif //LOGID_BACKEND_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 3eebe42..1dde8cf 100644 --- a/src/logid/backend/hidpp20/feature_defs.h +++ b/src/logid/backend/hidpp20/feature_defs.h @@ -1,129 +1,129 @@ -/* - * Copyright 2019-2023 PixlOne - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * 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 - -#include - -namespace logid::backend::hidpp20 { - 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, - SMART_SHIFT_V2 = 0x2111, - HIRES_SCROLLING = 0x2120, - HIRES_SCROLLING_V2 = 0x2121, // Referred to as Hi-res wheel in cvuchener/hidpp, seems to be V2? - LORES_SCROLLING = 0x2130, - THUMB_WHEEL = 0x2150, - 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 - }; - } - -} - +/* + * Copyright 2019-2023 PixlOne + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * 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 + +#include + +namespace logid::backend::hidpp20 { + 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, + SMART_SHIFT_V2 = 0x2111, + HIRES_SCROLLING = 0x2120, + HIRES_SCROLLING_V2 = 0x2121, // Referred to as Hi-res wheel in cvuchener/hidpp, seems to be V2? + LORES_SCROLLING = 0x2130, + THUMB_WHEEL = 0x2150, + 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/AdjustableDPI.cpp b/src/logid/backend/hidpp20/features/AdjustableDPI.cpp index 15dcd2b..805236a 100644 --- a/src/logid/backend/hidpp20/features/AdjustableDPI.cpp +++ b/src/logid/backend/hidpp20/features/AdjustableDPI.cpp @@ -1,82 +1,82 @@ -/* - * Copyright 2019-2023 PixlOne - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * 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 - -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); +/* + * Copyright 2019-2023 PixlOne + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * 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 + +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 index a7a7fdb..e5ee892 100644 --- a/src/logid/backend/hidpp20/features/AdjustableDPI.h +++ b/src/logid/backend/hidpp20/features/AdjustableDPI.h @@ -1,58 +1,58 @@ -/* - * Copyright 2019-2023 PixlOne - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * 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 -#include - -namespace logid::backend::hidpp20 { - class AdjustableDPI : public Feature { - public: - static const uint16_t ID = FeatureID::ADJUSTABLE_DPI; - - [[nodiscard]] uint16_t getID() final { return ID; } - - enum Function { - GetSensorCount = 0, - GetSensorDPIList = 1, - GetSensorDPI = 2, - SetSensorDPI = 3 - }; - - explicit 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 +/* + * Copyright 2019-2023 PixlOne + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * 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 +#include + +namespace logid::backend::hidpp20 { + class AdjustableDPI : public Feature { + public: + static const uint16_t ID = FeatureID::ADJUSTABLE_DPI; + + [[nodiscard]] uint16_t getID() final { return ID; } + + enum Function { + GetSensorCount = 0, + GetSensorDPIList = 1, + GetSensorDPI = 2, + SetSensorDPI = 3 + }; + + explicit 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 diff --git a/src/logid/backend/hidpp20/features/ChangeHost.cpp b/src/logid/backend/hidpp20/features/ChangeHost.cpp index f6ef377..f673669 100644 --- a/src/logid/backend/hidpp20/features/ChangeHost.cpp +++ b/src/logid/backend/hidpp20/features/ChangeHost.cpp @@ -1,75 +1,75 @@ -/* - * Copyright 2019-2023 PixlOne - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * 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 - -using namespace logid::backend::hidpp20; - -ChangeHost::ChangeHost(Device* dev) : Feature(dev, ID), _host_count(0) { -} - -ChangeHost::HostInfo ChangeHost::getHostInfo() { - std::vector params(0); - auto response = callFunction(GetHostInfo, params); - - HostInfo info{}; - info.hostCount = response[0]; - info.currentHost = response[1]; - info.enhancedHostSwitch = response[2] & 1; - - if (!_host_count) - _host_count = info.hostCount; - - return info; -} - -void ChangeHost::setHost(uint8_t host) { - /* Expect connection to be severed here, send without response - * - * Since there is no response, we have to emulate any kind of - * error that may be returned (i.e. InvalidArgument as per the docs) - */ - if (!_host_count) - getHostInfo(); - - if (host >= _host_count) - throw Error(hidpp20::Error::InvalidArgument, _device->deviceIndex()); - - std::vector params = {host}; - - callFunctionNoResponse(SetCurrentHost, params); -} - -[[maybe_unused]] -std::vector ChangeHost::getCookies() { - if (!_host_count) - getHostInfo(); - - std::vector params(0); - auto response = callFunction(GetCookies, params); - - response.resize(_host_count); - - return response; -} - -[[maybe_unused]] -void ChangeHost::setCookie(uint8_t host, uint8_t cookie) { - std::vector params = {host, cookie}; - callFunction(SetCookie, params); +/* + * Copyright 2019-2023 PixlOne + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * 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 + +using namespace logid::backend::hidpp20; + +ChangeHost::ChangeHost(Device* dev) : Feature(dev, ID), _host_count(0) { +} + +ChangeHost::HostInfo ChangeHost::getHostInfo() { + std::vector params(0); + auto response = callFunction(GetHostInfo, params); + + HostInfo info{}; + info.hostCount = response[0]; + info.currentHost = response[1]; + info.enhancedHostSwitch = response[2] & 1; + + if (!_host_count) + _host_count = info.hostCount; + + return info; +} + +void ChangeHost::setHost(uint8_t host) { + /* Expect connection to be severed here, send without response + * + * Since there is no response, we have to emulate any kind of + * error that may be returned (i.e. InvalidArgument as per the docs) + */ + if (!_host_count) + getHostInfo(); + + if (host >= _host_count) + throw Error(hidpp20::Error::InvalidArgument, _device->deviceIndex()); + + std::vector params = {host}; + + callFunctionNoResponse(SetCurrentHost, params); +} + +[[maybe_unused]] +std::vector ChangeHost::getCookies() { + if (!_host_count) + getHostInfo(); + + std::vector params(0); + auto response = callFunction(GetCookies, params); + + response.resize(_host_count); + + return response; +} + +[[maybe_unused]] +void ChangeHost::setCookie(uint8_t host, uint8_t cookie) { + std::vector params = {host, cookie}; + callFunction(SetCookie, params); } \ No newline at end of file diff --git a/src/logid/backend/hidpp20/features/ChangeHost.h b/src/logid/backend/hidpp20/features/ChangeHost.h index 14e5b33..1078767 100644 --- a/src/logid/backend/hidpp20/features/ChangeHost.h +++ b/src/logid/backend/hidpp20/features/ChangeHost.h @@ -1,59 +1,59 @@ -/* - * Copyright 2019-2023 PixlOne - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * 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_CHANGEHOST_H -#define LOGID_BACKEND_HIDPP20_FEATURE_CHANGEHOST_H - -#include -#include - -namespace logid::backend::hidpp20 { - class ChangeHost : public Feature { - public: - static const uint16_t ID = FeatureID::CHANGE_HOST; - - [[nodiscard]] uint16_t getID() final { return ID; } - - explicit ChangeHost(Device* dev); - - enum Function { - GetHostInfo = 0, - SetCurrentHost = 1, - GetCookies = 2, - SetCookie = 3 - }; - - struct HostInfo { - uint8_t hostCount; - uint8_t currentHost; - bool enhancedHostSwitch; - }; - - HostInfo getHostInfo(); - - void setHost(uint8_t host); - - [[maybe_unused]] [[maybe_unused]] std::vector getCookies(); - - [[maybe_unused]] [[maybe_unused]] void setCookie(uint8_t host, uint8_t cookie); - - private: - uint8_t _host_count; - }; -} - -#endif //LOGID_BACKEND_HIDPP20_FEATURE_CHANGEHOST_H +/* + * Copyright 2019-2023 PixlOne + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * 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_CHANGEHOST_H +#define LOGID_BACKEND_HIDPP20_FEATURE_CHANGEHOST_H + +#include +#include + +namespace logid::backend::hidpp20 { + class ChangeHost : public Feature { + public: + static const uint16_t ID = FeatureID::CHANGE_HOST; + + [[nodiscard]] uint16_t getID() final { return ID; } + + explicit ChangeHost(Device* dev); + + enum Function { + GetHostInfo = 0, + SetCurrentHost = 1, + GetCookies = 2, + SetCookie = 3 + }; + + struct HostInfo { + uint8_t hostCount; + uint8_t currentHost; + bool enhancedHostSwitch; + }; + + HostInfo getHostInfo(); + + void setHost(uint8_t host); + + [[maybe_unused]] [[maybe_unused]] std::vector getCookies(); + + [[maybe_unused]] [[maybe_unused]] void setCookie(uint8_t host, uint8_t cookie); + + private: + uint8_t _host_count; + }; +} + +#endif //LOGID_BACKEND_HIDPP20_FEATURE_CHANGEHOST_H diff --git a/src/logid/backend/hidpp20/features/DeviceName.cpp b/src/logid/backend/hidpp20/features/DeviceName.cpp index 3cf94f8..da44a4d 100644 --- a/src/logid/backend/hidpp20/features/DeviceName.cpp +++ b/src/logid/backend/hidpp20/features/DeviceName.cpp @@ -1,64 +1,64 @@ -/* - * Copyright 2019-2023 PixlOne - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * 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 - -using namespace logid::backend; -using namespace logid::backend::hidpp20; - -namespace { - 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 += (char) name_section[j]; - } - } - - return name; - } -} - -DeviceName::DeviceName(hidpp::Device* dev) : - EssentialFeature(dev, ID) { -} - -uint8_t DeviceName::getNameLength() { - std::vector params(0); - - auto response = this->callFunction(DeviceName::Function::GetLength, params); - - return response[0]; -} - -std::string DeviceName::getName() { - return _getName(getNameLength(), [this] - (std::vector params) -> std::vector { - return this->callFunction(DeviceName::Function::GetDeviceName, params); - }); +/* + * Copyright 2019-2023 PixlOne + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * 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 + +using namespace logid::backend; +using namespace logid::backend::hidpp20; + +namespace { + 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 += (char) name_section[j]; + } + } + + return name; + } +} + +DeviceName::DeviceName(hidpp::Device* dev) : + EssentialFeature(dev, ID) { +} + +uint8_t DeviceName::getNameLength() { + std::vector params(0); + + auto response = this->callFunction(DeviceName::Function::GetLength, params); + + return response[0]; +} + +std::string DeviceName::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 index 71a8c11..c72e4eb 100644 --- a/src/logid/backend/hidpp20/features/DeviceName.h +++ b/src/logid/backend/hidpp20/features/DeviceName.h @@ -1,45 +1,45 @@ -/* - * Copyright 2019-2023 PixlOne - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * 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 - -#include -#include - -namespace logid::backend::hidpp20 { - class DeviceName : public EssentialFeature { - public: - static const uint16_t ID = FeatureID::DEVICE_NAME; - - enum Function : uint8_t { - GetLength = 0, - GetDeviceName = 1 - }; - - [[nodiscard]] uint16_t getID() final { return ID; } - - explicit DeviceName(hidpp::Device* device); - - [[nodiscard]] uint8_t getNameLength(); - - [[nodiscard]] std::string getName(); - }; -} - +/* + * Copyright 2019-2023 PixlOne + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * 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 + +#include +#include + +namespace logid::backend::hidpp20 { + class DeviceName : public EssentialFeature { + public: + static const uint16_t ID = FeatureID::DEVICE_NAME; + + enum Function : uint8_t { + GetLength = 0, + GetDeviceName = 1 + }; + + [[nodiscard]] uint16_t getID() final { return ID; } + + explicit DeviceName(hidpp::Device* device); + + [[nodiscard]] uint8_t getNameLength(); + + [[nodiscard]] std::string getName(); + }; +} + #endif //LOGID_BACKEND_HIDPP20_FEATURE_DEVICENAME_H \ No newline at end of file diff --git a/src/logid/backend/hidpp20/features/FeatureSet.cpp b/src/logid/backend/hidpp20/features/FeatureSet.cpp index f626bbb..69a941b 100644 --- a/src/logid/backend/hidpp20/features/FeatureSet.cpp +++ b/src/logid/backend/hidpp20/features/FeatureSet.cpp @@ -1,49 +1,49 @@ -/* - * Copyright 2019-2023 PixlOne - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * 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 - -using namespace logid::backend::hidpp20; - -[[maybe_unused]] -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; -} - -[[maybe_unused]] -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; +/* + * Copyright 2019-2023 PixlOne + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * 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 + +using namespace logid::backend::hidpp20; + +[[maybe_unused]] +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; +} + +[[maybe_unused]] +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 index d8692a3..febb152 100644 --- a/src/logid/backend/hidpp20/features/FeatureSet.h +++ b/src/logid/backend/hidpp20/features/FeatureSet.h @@ -1,49 +1,49 @@ -/* - * Copyright 2019-2023 PixlOne - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * 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 -#include -#include - -namespace logid::backend::hidpp20 { - class FeatureSet : public Feature { - public: - static const uint16_t ID = FeatureID::FEATURE_SET; - - [[nodiscard]] uint16_t getID() final { return ID; } - - enum Function : uint8_t { - GetFeatureCount = 0, - GetFeature = 1 - }; - - [[maybe_unused]] [[maybe_unused]] - explicit FeatureSet(Device* device); - - [[nodiscard]] uint8_t getFeatureCount(); - - [[nodiscard]] uint16_t getFeature(uint8_t feature_index); - - [[maybe_unused]] - [[nodiscard]] std::map getFeatures(); - }; -} - -#endif //LOGID_BACKEND_HIDPP20_FEATURE_FEATURESET_H +/* + * Copyright 2019-2023 PixlOne + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * 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 +#include +#include + +namespace logid::backend::hidpp20 { + class FeatureSet : public Feature { + public: + static const uint16_t ID = FeatureID::FEATURE_SET; + + [[nodiscard]] uint16_t getID() final { return ID; } + + enum Function : uint8_t { + GetFeatureCount = 0, + GetFeature = 1 + }; + + [[maybe_unused]] [[maybe_unused]] + explicit FeatureSet(Device* device); + + [[nodiscard]] uint8_t getFeatureCount(); + + [[nodiscard]] uint16_t getFeature(uint8_t feature_index); + + [[maybe_unused]] + [[nodiscard]] std::map getFeatures(); + }; +} + +#endif //LOGID_BACKEND_HIDPP20_FEATURE_FEATURESET_H diff --git a/src/logid/backend/hidpp20/features/HiresScroll.cpp b/src/logid/backend/hidpp20/features/HiresScroll.cpp index 2a03190..6dc9918 100644 --- a/src/logid/backend/hidpp20/features/HiresScroll.cpp +++ b/src/logid/backend/hidpp20/features/HiresScroll.cpp @@ -1,68 +1,68 @@ -/* - * Copyright 2019-2023 PixlOne - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * 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 - -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); -} - -[[maybe_unused]] 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 = (int16_t) (report.paramBegin()[1] << 8 | report.paramBegin()[2]); - return status; -} - -[[maybe_unused]] -HiresScroll::RatchetState HiresScroll::ratchetSwitchEvent(const hidpp::Report& report) { - assert(report.function() == RatchetSwitch); - // Possible bad cast - return static_cast(report.paramBegin()[0]); +/* + * Copyright 2019-2023 PixlOne + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * 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 + +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); +} + +[[maybe_unused]] 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 = (int16_t) (report.paramBegin()[1] << 8 | report.paramBegin()[2]); + return status; +} + +[[maybe_unused]] +HiresScroll::RatchetState HiresScroll::ratchetSwitchEvent(const hidpp::Report& report) { + assert(report.function() == RatchetSwitch); + // 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 index 56eb0d4..827b447 100644 --- a/src/logid/backend/hidpp20/features/HiresScroll.h +++ b/src/logid/backend/hidpp20/features/HiresScroll.h @@ -1,90 +1,90 @@ -/* - * Copyright 2019-2023 PixlOne - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * 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 -#include -#include - -namespace logid::backend::hidpp20 { - class HiresScroll : public Feature { - public: - ///TODO: Hires scroll V1? - static const uint16_t ID = FeatureID::HIRES_SCROLLING_V2; - - uint16_t getID() final { 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 { - Invertible = 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; - int16_t deltaV; - }; - - explicit HiresScroll(Device* device); - - Capabilities getCapabilities(); - - uint8_t getMode(); - - void setMode(uint8_t mode); - - [[maybe_unused]] - bool getRatchetState(); - - static WheelStatus wheelMovementEvent(const hidpp::Report& report); - - [[maybe_unused]] - static RatchetState ratchetSwitchEvent(const hidpp::Report& report); - }; -} - -#endif //LOGID_BACKEND_HIDPP20_FEATURE_HIRESSCROLL_H +/* + * Copyright 2019-2023 PixlOne + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * 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 +#include +#include + +namespace logid::backend::hidpp20 { + class HiresScroll : public Feature { + public: + ///TODO: Hires scroll V1? + static const uint16_t ID = FeatureID::HIRES_SCROLLING_V2; + + uint16_t getID() final { 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 { + Invertible = 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; + int16_t deltaV; + }; + + explicit HiresScroll(Device* device); + + Capabilities getCapabilities(); + + uint8_t getMode(); + + void setMode(uint8_t mode); + + [[maybe_unused]] + bool getRatchetState(); + + static WheelStatus wheelMovementEvent(const hidpp::Report& report); + + [[maybe_unused]] + static RatchetState ratchetSwitchEvent(const hidpp::Report& report); + }; +} + +#endif //LOGID_BACKEND_HIDPP20_FEATURE_HIRESSCROLL_H diff --git a/src/logid/backend/hidpp20/features/ReprogControls.cpp b/src/logid/backend/hidpp20/features/ReprogControls.cpp index 22578b6..288c1d6 100644 --- a/src/logid/backend/hidpp20/features/ReprogControls.cpp +++ b/src/logid/backend/hidpp20/features/ReprogControls.cpp @@ -1,184 +1,184 @@ -/* - * Copyright 2019-2023 PixlOne - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * 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 - -using namespace logid::backend::hidpp20; - -// Define all the ReprogControls versions -#define DEFINE_REPROG(T, Base) \ - T::T(Device* dev, uint16_t _id) : Base(dev, _id) { } \ - T::T(Device* dev) : T(dev, ID) { } - -DEFINE_REPROG(ReprogControls, Feature) - -DEFINE_REPROG(ReprogControlsV2, ReprogControls) - -DEFINE_REPROG(ReprogControlsV2_2, ReprogControlsV2) - -DEFINE_REPROG(ReprogControlsV3, ReprogControlsV2_2) - -DEFINE_REPROG(ReprogControlsV4, ReprogControlsV3) - -template -std::shared_ptr make_reprog(Device* dev) { - try { - return std::make_shared(dev); - } catch (UnsupportedFeature& e) { - return {}; - } -} - -std::shared_ptr ReprogControls::autoVersion(Device* dev) { - if (auto v4 = make_reprog(dev)) { - return v4; - } else if (auto v3 = make_reprog(dev)) { - return v3; - } else if (auto v2_2 = make_reprog(dev)) { - return v2_2; - } else if (auto v2 = make_reprog(dev)) { - return v2; - } - - return std::make_shared(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; -} - -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; -} - -const std::map& -ReprogControls::getControls() const { - return _cids; -} - -ReprogControls::ControlInfo ReprogControls::getControlIdInfo(uint16_t cid) { - if (!_cids_initialized) - initCidMap(); - - auto it = _cids.find(cid); - if (it == _cids.end()) - throw Error(Error::InvalidArgument, _device->deviceIndex()); - else - return it->second; -} - -[[maybe_unused]] ReprogControls::ControlInfo ReprogControls::getControlReporting(uint16_t cid) { - // Emulate this function, only Reprog controls v4 supports this - auto info = getControlIdInfo(cid); - - ControlInfo report{}; - report.controlID = cid; - report.flags = 0; - if (info.flags & TemporaryDivertable) - report.flags |= TemporaryDiverted; - if (info.flags & PersistentlyDivertable) - report.flags |= PersistentlyDiverted; - if (info.additionalFlags & RawXY) - report.flags |= RawXYDiverted; - - return report; -} - -void ReprogControls::setControlReporting(uint16_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( - 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 = (int16_t) ((report.paramBegin()[0] << 8) | report.paramBegin()[1]); - move.y = (int16_t) ((report.paramBegin()[2] << 8) | report.paramBegin()[3]); - 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(uint16_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); +/* + * Copyright 2019-2023 PixlOne + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * 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 + +using namespace logid::backend::hidpp20; + +// Define all the ReprogControls versions +#define DEFINE_REPROG(T, Base) \ + T::T(Device* dev, uint16_t _id) : Base(dev, _id) { } \ + T::T(Device* dev) : T(dev, ID) { } + +DEFINE_REPROG(ReprogControls, Feature) + +DEFINE_REPROG(ReprogControlsV2, ReprogControls) + +DEFINE_REPROG(ReprogControlsV2_2, ReprogControlsV2) + +DEFINE_REPROG(ReprogControlsV3, ReprogControlsV2_2) + +DEFINE_REPROG(ReprogControlsV4, ReprogControlsV3) + +template +std::shared_ptr make_reprog(Device* dev) { + try { + return std::make_shared(dev); + } catch (UnsupportedFeature& e) { + return {}; + } +} + +std::shared_ptr ReprogControls::autoVersion(Device* dev) { + if (auto v4 = make_reprog(dev)) { + return v4; + } else if (auto v3 = make_reprog(dev)) { + return v3; + } else if (auto v2_2 = make_reprog(dev)) { + return v2_2; + } else if (auto v2 = make_reprog(dev)) { + return v2; + } + + return std::make_shared(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; +} + +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; +} + +const std::map& +ReprogControls::getControls() const { + return _cids; +} + +ReprogControls::ControlInfo ReprogControls::getControlIdInfo(uint16_t cid) { + if (!_cids_initialized) + initCidMap(); + + auto it = _cids.find(cid); + if (it == _cids.end()) + throw Error(Error::InvalidArgument, _device->deviceIndex()); + else + return it->second; +} + +[[maybe_unused]] ReprogControls::ControlInfo ReprogControls::getControlReporting(uint16_t cid) { + // Emulate this function, only Reprog controls v4 supports this + auto info = getControlIdInfo(cid); + + ControlInfo report{}; + report.controlID = cid; + report.flags = 0; + if (info.flags & TemporaryDivertable) + report.flags |= TemporaryDiverted; + if (info.flags & PersistentlyDivertable) + report.flags |= PersistentlyDiverted; + if (info.additionalFlags & RawXY) + report.flags |= RawXYDiverted; + + return report; +} + +void ReprogControls::setControlReporting(uint16_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( + 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 = (int16_t) ((report.paramBegin()[0] << 8) | report.paramBegin()[1]); + move.y = (int16_t) ((report.paramBegin()[2] << 8) | report.paramBegin()[3]); + 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(uint16_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 49da7e9..c4dcb9f 100644 --- a/src/logid/backend/hidpp20/features/ReprogControls.h +++ b/src/logid/backend/hidpp20/features/ReprogControls.h @@ -1,175 +1,175 @@ -/* - * Copyright 2019-2023 PixlOne - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * 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 -#include -#include -#include -#include -#include - -namespace logid::backend::hidpp20 { - class ReprogControls : public Feature { - public: - enum Function { - GetControlCount = 0, - GetControlInfo = 1, - GetControlReporting = 2, - SetControlReporting = 3 - }; - enum Event { - DivertedButtonEvent = 0, - DivertedRawXYEvent = 1 - }; - - 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, - PersistentlyDivertable = 1 << 6, - Virtual = 1 << 7 - }; - enum ControlInfoAdditionalFlags : uint8_t { - RawXY = 1 << 0 - }; - - enum ControlReportingFlags : uint8_t { - TemporaryDiverted = 1 << 0, - ChangeTemporaryDivert = 1 << 1, - PersistentlyDiverted = 1 << 2, - ChangePersistentDivert [[maybe_unused]] = 1 << 3, - RawXYDiverted = 1 << 4, - ChangeRawXYDivert = 1 << 5 - }; - - struct Move { - int16_t x; - int16_t y; - }; - - static const uint16_t ID = FeatureID::REPROG_CONTROLS; - - [[nodiscard]] uint16_t getID() override { return ID; } - - [[nodiscard]] virtual bool supportsRawXY() { return false; } - - explicit ReprogControls(Device* dev); - - [[nodiscard]] virtual uint8_t getControlCount(); - - [[nodiscard]] virtual ControlInfo getControlInfo(uint8_t cid); - - [[nodiscard]] virtual ControlInfo getControlIdInfo(uint16_t cid); - - virtual void initCidMap(); - - [[nodiscard]] const std::map& getControls() const; - - // Only controlId and flags will be set - [[maybe_unused]] - [[nodiscard]] virtual ControlInfo getControlReporting(uint16_t cid); - - // Only controlId (for remap) and flags will be read - virtual void setControlReporting(uint16_t cid, ControlInfo info); - - [[nodiscard]] static std::set divertedButtonEvent(const hidpp::Report& report); - - [[nodiscard]] static Move divertedRawXYEvent(const hidpp::Report& report); - - [[nodiscard]] static std::shared_ptr autoVersion(Device* dev); - - protected: - ReprogControls(Device* dev, uint16_t _id); - - std::map _cids; - bool _cids_initialized = false; - std::mutex _cids_populating; - }; - - class ReprogControlsV2 : public ReprogControls { - public: - static const uint16_t ID = FeatureID::REPROG_CONTROLS_V2; - - [[nodiscard]] 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; - - [[nodiscard]] 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; - - [[nodiscard]] 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; - - [[nodiscard]] uint16_t getID() final { return ID; } - - [[nodiscard]] bool supportsRawXY() override { return true; } - - [[nodiscard]] ControlInfo getControlReporting(uint16_t cid) override; - - void setControlReporting(uint16_t cid, ControlInfo info) override; - - explicit ReprogControlsV4(Device* dev); - - protected: - [[maybe_unused]] - ReprogControlsV4(Device* dev, uint16_t _id); - }; -} - -#endif //LOGID_BACKEND_HIDPP20_FEATURE_REPROGCONTROLS_H +/* + * Copyright 2019-2023 PixlOne + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * 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 +#include +#include +#include +#include +#include + +namespace logid::backend::hidpp20 { + class ReprogControls : public Feature { + public: + enum Function { + GetControlCount = 0, + GetControlInfo = 1, + GetControlReporting = 2, + SetControlReporting = 3 + }; + enum Event { + DivertedButtonEvent = 0, + DivertedRawXYEvent = 1 + }; + + 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, + PersistentlyDivertable = 1 << 6, + Virtual = 1 << 7 + }; + enum ControlInfoAdditionalFlags : uint8_t { + RawXY = 1 << 0 + }; + + enum ControlReportingFlags : uint8_t { + TemporaryDiverted = 1 << 0, + ChangeTemporaryDivert = 1 << 1, + PersistentlyDiverted = 1 << 2, + ChangePersistentDivert [[maybe_unused]] = 1 << 3, + RawXYDiverted = 1 << 4, + ChangeRawXYDivert = 1 << 5 + }; + + struct Move { + int16_t x; + int16_t y; + }; + + static const uint16_t ID = FeatureID::REPROG_CONTROLS; + + [[nodiscard]] uint16_t getID() override { return ID; } + + [[nodiscard]] virtual bool supportsRawXY() { return false; } + + explicit ReprogControls(Device* dev); + + [[nodiscard]] virtual uint8_t getControlCount(); + + [[nodiscard]] virtual ControlInfo getControlInfo(uint8_t cid); + + [[nodiscard]] virtual ControlInfo getControlIdInfo(uint16_t cid); + + virtual void initCidMap(); + + [[nodiscard]] const std::map& getControls() const; + + // Only controlId and flags will be set + [[maybe_unused]] + [[nodiscard]] virtual ControlInfo getControlReporting(uint16_t cid); + + // Only controlId (for remap) and flags will be read + virtual void setControlReporting(uint16_t cid, ControlInfo info); + + [[nodiscard]] static std::set divertedButtonEvent(const hidpp::Report& report); + + [[nodiscard]] static Move divertedRawXYEvent(const hidpp::Report& report); + + [[nodiscard]] static std::shared_ptr autoVersion(Device* dev); + + protected: + ReprogControls(Device* dev, uint16_t _id); + + std::map _cids; + bool _cids_initialized = false; + std::mutex _cids_populating; + }; + + class ReprogControlsV2 : public ReprogControls { + public: + static const uint16_t ID = FeatureID::REPROG_CONTROLS_V2; + + [[nodiscard]] 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; + + [[nodiscard]] 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; + + [[nodiscard]] 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; + + [[nodiscard]] uint16_t getID() final { return ID; } + + [[nodiscard]] bool supportsRawXY() override { return true; } + + [[nodiscard]] ControlInfo getControlReporting(uint16_t cid) override; + + void setControlReporting(uint16_t cid, ControlInfo info) override; + + explicit ReprogControlsV4(Device* dev); + + protected: + [[maybe_unused]] + ReprogControlsV4(Device* dev, uint16_t _id); + }; +} + +#endif //LOGID_BACKEND_HIDPP20_FEATURE_REPROGCONTROLS_H diff --git a/src/logid/backend/hidpp20/features/Reset.cpp b/src/logid/backend/hidpp20/features/Reset.cpp index 7cc0447..d06a4b0 100644 --- a/src/logid/backend/hidpp20/features/Reset.cpp +++ b/src/logid/backend/hidpp20/features/Reset.cpp @@ -1,39 +1,39 @@ -/* - * Copyright 2019-2023 PixlOne - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * 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 - -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); +/* + * Copyright 2019-2023 PixlOne + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * 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 + +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 index 4bdb848..3d45dc8 100644 --- a/src/logid/backend/hidpp20/features/Reset.h +++ b/src/logid/backend/hidpp20/features/Reset.h @@ -1,44 +1,44 @@ -/* - * Copyright 2019-2023 PixlOne - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * 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 -#include - -namespace logid::backend::hidpp20 { - class Reset : public Feature { - public: - static const uint16_t ID = FeatureID::RESET; - - [[nodiscard]] uint16_t getID() final { 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 +/* + * Copyright 2019-2023 PixlOne + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * 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 +#include + +namespace logid::backend::hidpp20 { + class Reset : public Feature { + public: + static const uint16_t ID = FeatureID::RESET; + + [[nodiscard]] uint16_t getID() final { 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 diff --git a/src/logid/backend/hidpp20/features/Root.cpp b/src/logid/backend/hidpp20/features/Root.cpp index 0cc3e36..4f1eb21 100644 --- a/src/logid/backend/hidpp20/features/Root.cpp +++ b/src/logid/backend/hidpp20/features/Root.cpp @@ -1,81 +1,81 @@ -/* - * Copyright 2019-2023 PixlOne - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * 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 - -using namespace logid::backend::hidpp20; - -namespace { - std::vector _genGetFeatureParams(uint16_t feature_id) { - std::vector params(2); - params[0] = feature_id & 0xff; - params[1] = (feature_id >> 8) & 0xff; - return 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] & Root::FeatureFlag::Hidden; - info.obsolete = response[1] & Root::FeatureFlag::Obsolete; - info.internal = response[1] & Root::FeatureFlag::Internal; - - return info; - } -} - -Root::Root(hidpp::Device* dev) : EssentialFeature(dev, ID) { -} - -feature_info Root::getFeature(uint16_t feature_id) { - auto params = _genGetFeatureParams(feature_id); - 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; - } -} - -uint8_t Root::ping(uint8_t byte) { - std::vector params(3); - params[2] = byte; - - auto response = this->callFunction(Root::Function::Ping, params); - - return response[2]; -} - -std::tuple Root::getVersion() { - std::vector params(0); - auto response = this->callFunction(Root::Function::Ping, params); - - if (response[0] == 0x11) - return std::make_tuple(1, 0); - - return std::make_tuple(response[0], response[1]); +/* + * Copyright 2019-2023 PixlOne + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * 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 + +using namespace logid::backend::hidpp20; + +namespace { + std::vector _genGetFeatureParams(uint16_t feature_id) { + std::vector params(2); + params[0] = feature_id & 0xff; + params[1] = (feature_id >> 8) & 0xff; + return 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] & Root::FeatureFlag::Hidden; + info.obsolete = response[1] & Root::FeatureFlag::Obsolete; + info.internal = response[1] & Root::FeatureFlag::Internal; + + return info; + } +} + +Root::Root(hidpp::Device* dev) : EssentialFeature(dev, ID) { +} + +feature_info Root::getFeature(uint16_t feature_id) { + auto params = _genGetFeatureParams(feature_id); + 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; + } +} + +uint8_t Root::ping(uint8_t byte) { + std::vector params(3); + params[2] = byte; + + auto response = this->callFunction(Root::Function::Ping, params); + + return response[2]; +} + +std::tuple Root::getVersion() { + std::vector params(0); + auto response = this->callFunction(Root::Function::Ping, params); + + if (response[0] == 0x11) + return std::make_tuple(1, 0); + + 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 89017c4..49986fa 100644 --- a/src/logid/backend/hidpp20/features/Root.h +++ b/src/logid/backend/hidpp20/features/Root.h @@ -1,54 +1,54 @@ -/* - * Copyright 2019-2023 PixlOne - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * 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 - -#include -#include - -namespace logid::backend::hidpp20 { - - class Root : public EssentialFeature { - public: - static const uint16_t ID = FeatureID::ROOT; - - uint16_t getID() final { return ID; } - - explicit Root(hidpp::Device* device); - - enum Function : uint8_t { - GetFeature = 0, - Ping = 1 - }; - - feature_info getFeature(uint16_t feature_id); - - uint8_t ping(uint8_t byte); - - std::tuple getVersion(); - - enum FeatureFlag : uint8_t { - Obsolete = 1 << 7, - Hidden = 1 << 6, - Internal = 1 << 5 - }; - }; -} - +/* + * Copyright 2019-2023 PixlOne + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * 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 + +#include +#include + +namespace logid::backend::hidpp20 { + + class Root : public EssentialFeature { + public: + static const uint16_t ID = FeatureID::ROOT; + + uint16_t getID() final { return ID; } + + explicit Root(hidpp::Device* device); + + enum Function : uint8_t { + GetFeature = 0, + Ping = 1 + }; + + feature_info getFeature(uint16_t feature_id); + + uint8_t ping(uint8_t byte); + + std::tuple getVersion(); + + 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/hidpp20/features/SmartShift.cpp b/src/logid/backend/hidpp20/features/SmartShift.cpp index 5237582..a3b1d04 100644 --- a/src/logid/backend/hidpp20/features/SmartShift.cpp +++ b/src/logid/backend/hidpp20/features/SmartShift.cpp @@ -1,125 +1,125 @@ -/* - * Copyright 2019-2023 PixlOne - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * 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 - -using namespace logid::backend::hidpp20; - -SmartShift::SmartShift(Device* dev) : SmartShift(dev, ID) { -} - -SmartShift::SmartShift(Device* dev, uint16_t feature_id) : - Feature(dev, feature_id) { -} - -SmartShiftV2::SmartShiftV2(Device* dev) : SmartShift(dev, ID) { -} - -template -std::shared_ptr make_smartshift(Device* dev) { - try { - return std::make_shared(dev); - } catch (UnsupportedFeature& e) { - return {}; - } -} - -std::shared_ptr SmartShift::autoVersion(Device* dev) { - if (auto v2 = make_smartshift(dev)) - return v2; - - return std::make_shared(dev); -} - -SmartShift::Status SmartShift::getStatus() { - std::vector params(0); - - auto response = callFunction(GetStatus, params); - - return { - .active = static_cast(response[0] - 1), - .autoDisengage = response[1], - .torque = 0, - .setActive = false, - .setAutoDisengage = false, - .setTorque = false - }; -} - -SmartShift::Defaults SmartShift::getDefaults() { - std::vector params(0); - - auto response = callFunction(GetStatus, params); - - return { - .autoDisengage = response[2], - .torque = 0, - .maxForce = 0, - }; -} - -void SmartShift::setStatus(Status status) { - std::vector params(3); - if (status.setActive) - params[0] = status.active + 1; - if (status.setAutoDisengage) - params[1] = status.autoDisengage; - callFunction(SetStatus, params); -} - -SmartShift::Defaults SmartShiftV2::getDefaults() { - std::vector params(0); - auto response = callFunction(GetCapabilities, params); - - return { - .autoDisengage = response[1], - .torque = response[2], - .maxForce = response[3], - }; -} - -SmartShift::Status SmartShiftV2::getStatus() { - std::vector params(0); - auto response = callFunction(GetStatus, params); - - return { - .active = static_cast(response[0] - 1), - .autoDisengage = response[1], - .torque = response[2], - .setActive = false, .setAutoDisengage = false, .setTorque = false, - }; -} - -void SmartShiftV2::setStatus(Status status) { - std::vector params(3); - if (status.setActive) - params[0] = status.active + 1; - if (status.setAutoDisengage) - params[1] = status.autoDisengage; - if (status.setTorque) - params[2] = status.torque; - - callFunction(SetStatus, params); -} - -bool SmartShiftV2::supportsTorque() { - std::vector params(0); - auto response = callFunction(GetCapabilities, params); - - return static_cast(response[0] & 1); -} - +/* + * Copyright 2019-2023 PixlOne + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * 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 + +using namespace logid::backend::hidpp20; + +SmartShift::SmartShift(Device* dev) : SmartShift(dev, ID) { +} + +SmartShift::SmartShift(Device* dev, uint16_t feature_id) : + Feature(dev, feature_id) { +} + +SmartShiftV2::SmartShiftV2(Device* dev) : SmartShift(dev, ID) { +} + +template +std::shared_ptr make_smartshift(Device* dev) { + try { + return std::make_shared(dev); + } catch (UnsupportedFeature& e) { + return {}; + } +} + +std::shared_ptr SmartShift::autoVersion(Device* dev) { + if (auto v2 = make_smartshift(dev)) + return v2; + + return std::make_shared(dev); +} + +SmartShift::Status SmartShift::getStatus() { + std::vector params(0); + + auto response = callFunction(GetStatus, params); + + return { + .active = static_cast(response[0] - 1), + .autoDisengage = response[1], + .torque = 0, + .setActive = false, + .setAutoDisengage = false, + .setTorque = false + }; +} + +SmartShift::Defaults SmartShift::getDefaults() { + std::vector params(0); + + auto response = callFunction(GetStatus, params); + + return { + .autoDisengage = response[2], + .torque = 0, + .maxForce = 0, + }; +} + +void SmartShift::setStatus(Status status) { + std::vector params(3); + if (status.setActive) + params[0] = status.active + 1; + if (status.setAutoDisengage) + params[1] = status.autoDisengage; + callFunction(SetStatus, params); +} + +SmartShift::Defaults SmartShiftV2::getDefaults() { + std::vector params(0); + auto response = callFunction(GetCapabilities, params); + + return { + .autoDisengage = response[1], + .torque = response[2], + .maxForce = response[3], + }; +} + +SmartShift::Status SmartShiftV2::getStatus() { + std::vector params(0); + auto response = callFunction(GetStatus, params); + + return { + .active = static_cast(response[0] - 1), + .autoDisengage = response[1], + .torque = response[2], + .setActive = false, .setAutoDisengage = false, .setTorque = false, + }; +} + +void SmartShiftV2::setStatus(Status status) { + std::vector params(3); + if (status.setActive) + params[0] = status.active + 1; + if (status.setAutoDisengage) + params[1] = status.autoDisengage; + if (status.setTorque) + params[2] = status.torque; + + callFunction(SetStatus, params); +} + +bool SmartShiftV2::supportsTorque() { + std::vector params(0); + auto response = callFunction(GetCapabilities, params); + + return static_cast(response[0] & 1); +} + diff --git a/src/logid/backend/hidpp20/features/SmartShift.h b/src/logid/backend/hidpp20/features/SmartShift.h index 70e3357..13767cc 100644 --- a/src/logid/backend/hidpp20/features/SmartShift.h +++ b/src/logid/backend/hidpp20/features/SmartShift.h @@ -1,90 +1,90 @@ -/* - * Copyright 2019-2023 PixlOne - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * 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 -#include -#include - -namespace logid::backend::hidpp20 { - class SmartShift : public Feature { - public: - static const uint16_t ID = FeatureID::SMART_SHIFT; - - uint16_t getID() override { return ID; } - - enum Function { - GetStatus = 0, - SetStatus = 1 - }; - - explicit SmartShift(Device* dev); - - struct Defaults { - uint8_t autoDisengage; - uint8_t torque; - uint8_t maxForce; - }; - - struct Status { - bool active; - uint8_t autoDisengage; - uint8_t torque; - bool setActive, setAutoDisengage, setTorque; - }; - - [[nodiscard]] virtual bool supportsTorque() { return false; } - - [[nodiscard]] virtual Defaults getDefaults(); - - [[nodiscard]] virtual Status getStatus(); - - virtual void setStatus(Status status); - - [[nodiscard]] static std::shared_ptr autoVersion(Device* dev); - - protected: - SmartShift(Device* dev, uint16_t feature_id); - }; - - class SmartShiftV2 : public SmartShift - { - public: - static const uint16_t ID = FeatureID::SMART_SHIFT_V2; - uint16_t getID() final { return ID; } - - enum Function { - GetCapabilities = 0, - GetStatus = 1, - SetStatus = 2 - }; - - explicit SmartShiftV2(Device* dev); - - [[nodiscard]] bool supportsTorque() final; - - [[nodiscard]] Defaults getDefaults() final; - - [[nodiscard]] Status getStatus() final; - - void setStatus(Status status) final; - }; -} - -#endif //LOGID_BACKEND_HIDPP20_FEATURE_SMARTSHIFT_H +/* + * Copyright 2019-2023 PixlOne + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * 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 +#include +#include + +namespace logid::backend::hidpp20 { + class SmartShift : public Feature { + public: + static const uint16_t ID = FeatureID::SMART_SHIFT; + + uint16_t getID() override { return ID; } + + enum Function { + GetStatus = 0, + SetStatus = 1 + }; + + explicit SmartShift(Device* dev); + + struct Defaults { + uint8_t autoDisengage; + uint8_t torque; + uint8_t maxForce; + }; + + struct Status { + bool active; + uint8_t autoDisengage; + uint8_t torque; + bool setActive, setAutoDisengage, setTorque; + }; + + [[nodiscard]] virtual bool supportsTorque() { return false; } + + [[nodiscard]] virtual Defaults getDefaults(); + + [[nodiscard]] virtual Status getStatus(); + + virtual void setStatus(Status status); + + [[nodiscard]] static std::shared_ptr autoVersion(Device* dev); + + protected: + SmartShift(Device* dev, uint16_t feature_id); + }; + + class SmartShiftV2 : public SmartShift + { + public: + static const uint16_t ID = FeatureID::SMART_SHIFT_V2; + uint16_t getID() final { return ID; } + + enum Function { + GetCapabilities = 0, + GetStatus = 1, + SetStatus = 2 + }; + + explicit SmartShiftV2(Device* dev); + + [[nodiscard]] bool supportsTorque() final; + + [[nodiscard]] Defaults getDefaults() final; + + [[nodiscard]] Status getStatus() final; + + void setStatus(Status status) final; + }; +} + +#endif //LOGID_BACKEND_HIDPP20_FEATURE_SMARTSHIFT_H diff --git a/src/logid/backend/hidpp20/features/ThumbWheel.cpp b/src/logid/backend/hidpp20/features/ThumbWheel.cpp index fe0c3ba..24fd9e1 100644 --- a/src/logid/backend/hidpp20/features/ThumbWheel.cpp +++ b/src/logid/backend/hidpp20/features/ThumbWheel.cpp @@ -1,79 +1,79 @@ -/* - * Copyright 2019-2023 PixlOne - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * 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 - -using namespace logid::backend::hidpp20; - -ThumbWheel::ThumbWheel(Device* dev) : Feature(dev, ID) { -} - -ThumbWheel::ThumbwheelInfo ThumbWheel::getInfo() { - std::vector params(0), response; - ThumbwheelInfo info{}; - response = callFunction(GetInfo, params); - - info.nativeRes = response[1]; - info.nativeRes |= (response[0] << 8); - info.divertedRes = response[3]; - info.divertedRes |= (response[2] << 8); - info.defaultDirection = response[4] ? 1 : -1; /* 1 increment to the right */ - info.capabilities = response[5]; - info.timeElapsed = response[7]; - info.timeElapsed |= response[6] << 8; - - return info; -} - -ThumbWheel::ThumbwheelStatus ThumbWheel::getStatus() { - std::vector params(0), response; - ThumbwheelStatus status{}; - response = callFunction(GetStatus, params); - - status.diverted = response[0]; - status.inverted = response[1] & 1; - status.touch = response[1] & (1 << 1); - status.proxy = response[1] & (1 << 2); - - return status; -} - -ThumbWheel::ThumbwheelStatus ThumbWheel::setStatus(bool divert, bool invert) { - std::vector params(2), response; - ThumbwheelStatus status{}; - params[0] = divert; - params[1] = invert; - - response = callFunction(SetReporting, params); - status.diverted = response[0]; - status.inverted = response[1] & 1; - - return status; -} - -ThumbWheel::ThumbwheelEvent ThumbWheel::thumbwheelEvent(const hidpp::Report& report) { - assert(report.function() == Event); - ThumbwheelEvent event{}; - event.rotation = (int16_t) ((report.paramBegin()[0] << 8) | report.paramBegin()[1]); - event.timestamp = report.paramBegin()[3]; - event.timestamp |= report.paramBegin()[2] << 8; - event.rotationStatus = static_cast(report.paramBegin()[4]); - event.flags = report.paramBegin()[5]; - return event; +/* + * Copyright 2019-2023 PixlOne + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * 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 + +using namespace logid::backend::hidpp20; + +ThumbWheel::ThumbWheel(Device* dev) : Feature(dev, ID) { +} + +ThumbWheel::ThumbwheelInfo ThumbWheel::getInfo() { + std::vector params(0), response; + ThumbwheelInfo info{}; + response = callFunction(GetInfo, params); + + info.nativeRes = response[1]; + info.nativeRes |= (response[0] << 8); + info.divertedRes = response[3]; + info.divertedRes |= (response[2] << 8); + info.defaultDirection = response[4] ? 1 : -1; /* 1 increment to the right */ + info.capabilities = response[5]; + info.timeElapsed = response[7]; + info.timeElapsed |= response[6] << 8; + + return info; +} + +ThumbWheel::ThumbwheelStatus ThumbWheel::getStatus() { + std::vector params(0), response; + ThumbwheelStatus status{}; + response = callFunction(GetStatus, params); + + status.diverted = response[0]; + status.inverted = response[1] & 1; + status.touch = response[1] & (1 << 1); + status.proxy = response[1] & (1 << 2); + + return status; +} + +ThumbWheel::ThumbwheelStatus ThumbWheel::setStatus(bool divert, bool invert) { + std::vector params(2), response; + ThumbwheelStatus status{}; + params[0] = divert; + params[1] = invert; + + response = callFunction(SetReporting, params); + status.diverted = response[0]; + status.inverted = response[1] & 1; + + return status; +} + +ThumbWheel::ThumbwheelEvent ThumbWheel::thumbwheelEvent(const hidpp::Report& report) { + assert(report.function() == Event); + ThumbwheelEvent event{}; + event.rotation = (int16_t) ((report.paramBegin()[0] << 8) | report.paramBegin()[1]); + event.timestamp = report.paramBegin()[3]; + event.timestamp |= report.paramBegin()[2] << 8; + event.rotationStatus = static_cast(report.paramBegin()[4]); + event.flags = report.paramBegin()[5]; + return event; } \ No newline at end of file diff --git a/src/logid/backend/hidpp20/features/ThumbWheel.h b/src/logid/backend/hidpp20/features/ThumbWheel.h index 12b95a1..250496d 100644 --- a/src/logid/backend/hidpp20/features/ThumbWheel.h +++ b/src/logid/backend/hidpp20/features/ThumbWheel.h @@ -1,90 +1,90 @@ -/* - * Copyright 2019-2023 PixlOne - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * 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_THUMBWHEEL_H -#define LOGID_BACKEND_HIDPP20_FEATURE_THUMBWHEEL_H - -#include -#include -#include - -namespace logid::backend::hidpp20 { - class ThumbWheel : public Feature { - public: - static const uint16_t ID = FeatureID::THUMB_WHEEL; - - uint16_t getID() final { return ID; } - - enum Function { - GetInfo = 0, - GetStatus = 1, - SetReporting = 2 - }; - - enum Event { - Event = 0 /* Catch-all event */ - }; - - explicit ThumbWheel(Device* dev); - - enum Capabilities : uint8_t { - Timestamp = 1, - Touch = 1 << 1, - Proxy = 1 << 2, - SingleTap = 1 << 3 - }; - - struct ThumbwheelInfo { - uint16_t nativeRes; - uint16_t divertedRes; - int8_t defaultDirection; - uint8_t capabilities; - uint16_t timeElapsed; - }; - - struct ThumbwheelStatus { - bool diverted; - bool inverted; - bool touch; - bool proxy; - }; - - enum RotationStatus : uint8_t { - Inactive = 0, - Start = 1, - Active = 2, - Stop = 3 - }; - - struct ThumbwheelEvent { - int16_t rotation; - uint16_t timestamp; - RotationStatus rotationStatus; - uint8_t flags; - }; - - [[nodiscard]] ThumbwheelInfo getInfo(); - - [[nodiscard]] ThumbwheelStatus getStatus(); - - ThumbwheelStatus setStatus(bool divert, bool invert); - - [[nodiscard]] static ThumbwheelEvent thumbwheelEvent(const hidpp::Report& report); - }; -} - -#endif //LOGID_BACKEND_HIDPP20_FEATURE_THUMBWHEEL_H +/* + * Copyright 2019-2023 PixlOne + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * 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_THUMBWHEEL_H +#define LOGID_BACKEND_HIDPP20_FEATURE_THUMBWHEEL_H + +#include +#include +#include + +namespace logid::backend::hidpp20 { + class ThumbWheel : public Feature { + public: + static const uint16_t ID = FeatureID::THUMB_WHEEL; + + uint16_t getID() final { return ID; } + + enum Function { + GetInfo = 0, + GetStatus = 1, + SetReporting = 2 + }; + + enum Event { + Event = 0 /* Catch-all event */ + }; + + explicit ThumbWheel(Device* dev); + + enum Capabilities : uint8_t { + Timestamp = 1, + Touch = 1 << 1, + Proxy = 1 << 2, + SingleTap = 1 << 3 + }; + + struct ThumbwheelInfo { + uint16_t nativeRes; + uint16_t divertedRes; + int8_t defaultDirection; + uint8_t capabilities; + uint16_t timeElapsed; + }; + + struct ThumbwheelStatus { + bool diverted; + bool inverted; + bool touch; + bool proxy; + }; + + enum RotationStatus : uint8_t { + Inactive = 0, + Start = 1, + Active = 2, + Stop = 3 + }; + + struct ThumbwheelEvent { + int16_t rotation; + uint16_t timestamp; + RotationStatus rotationStatus; + uint8_t flags; + }; + + [[nodiscard]] ThumbwheelInfo getInfo(); + + [[nodiscard]] ThumbwheelStatus getStatus(); + + ThumbwheelStatus setStatus(bool divert, bool invert); + + [[nodiscard]] static ThumbwheelEvent thumbwheelEvent(const hidpp::Report& report); + }; +} + +#endif //LOGID_BACKEND_HIDPP20_FEATURE_THUMBWHEEL_H diff --git a/src/logid/backend/hidpp20/features/WirelessDeviceStatus.cpp b/src/logid/backend/hidpp20/features/WirelessDeviceStatus.cpp index 42c36a9..7de6328 100644 --- a/src/logid/backend/hidpp20/features/WirelessDeviceStatus.cpp +++ b/src/logid/backend/hidpp20/features/WirelessDeviceStatus.cpp @@ -1,35 +1,35 @@ -/* - * Copyright 2019-2023 PixlOne - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * 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 - -using namespace logid::backend::hidpp20; - -WirelessDeviceStatus::WirelessDeviceStatus(Device* dev) : Feature(dev, ID) { -} - -WirelessDeviceStatus::Status WirelessDeviceStatus::statusBroadcastEvent( - const hidpp::Report& report) { - assert(report.function() == StatusBroadcast); - Status status = {}; - auto params = report.paramBegin(); - status.reconnection = params[0]; - status.reconfNeeded = params[1]; - status.powerSwitch = params[2]; - return status; +/* + * Copyright 2019-2023 PixlOne + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * 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 + +using namespace logid::backend::hidpp20; + +WirelessDeviceStatus::WirelessDeviceStatus(Device* dev) : Feature(dev, ID) { +} + +WirelessDeviceStatus::Status WirelessDeviceStatus::statusBroadcastEvent( + const hidpp::Report& report) { + assert(report.function() == StatusBroadcast); + Status status = {}; + auto params = report.paramBegin(); + status.reconnection = params[0]; + status.reconfNeeded = params[1]; + status.powerSwitch = params[2]; + return status; } \ No newline at end of file diff --git a/src/logid/backend/hidpp20/features/WirelessDeviceStatus.h b/src/logid/backend/hidpp20/features/WirelessDeviceStatus.h index 74ec8c9..7b966b1 100644 --- a/src/logid/backend/hidpp20/features/WirelessDeviceStatus.h +++ b/src/logid/backend/hidpp20/features/WirelessDeviceStatus.h @@ -1,48 +1,48 @@ -/* - * Copyright 2019-2023 PixlOne - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * 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_WIRELESSDEVICESTATUS_H -#define LOGID_BACKEND_HIDPP20_FEATURE_WIRELESSDEVICESTATUS_H - -#include -#include -#include - -namespace logid::backend::hidpp20 { - class WirelessDeviceStatus : public Feature { - public: - static constexpr uint16_t ID = FeatureID::WIRELESS_DEVICE_STATUS; - - [[nodiscard]] uint16_t getID() final { return ID; } - - explicit WirelessDeviceStatus(Device* dev); - - enum Event : uint8_t { - StatusBroadcast = 0 - }; - - struct Status { - bool reconnection; - bool reconfNeeded; - bool powerSwitch; - }; - - static Status statusBroadcastEvent(const hidpp::Report& report); - }; -} - -#endif //LOGID_BACKEND_HIDPP20_FEATURE_WIRELESSDEVICESTATUS_H +/* + * Copyright 2019-2023 PixlOne + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * 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_WIRELESSDEVICESTATUS_H +#define LOGID_BACKEND_HIDPP20_FEATURE_WIRELESSDEVICESTATUS_H + +#include +#include +#include + +namespace logid::backend::hidpp20 { + class WirelessDeviceStatus : public Feature { + public: + static constexpr uint16_t ID = FeatureID::WIRELESS_DEVICE_STATUS; + + [[nodiscard]] uint16_t getID() final { return ID; } + + explicit WirelessDeviceStatus(Device* dev); + + enum Event : uint8_t { + StatusBroadcast = 0 + }; + + struct Status { + bool reconnection; + bool reconfNeeded; + bool powerSwitch; + }; + + static Status statusBroadcastEvent(const hidpp::Report& report); + }; +} + +#endif //LOGID_BACKEND_HIDPP20_FEATURE_WIRELESSDEVICESTATUS_H diff --git a/src/logid/backend/raw/DeviceMonitor.cpp b/src/logid/backend/raw/DeviceMonitor.cpp index 7b8ae01..8bccc77 100644 --- a/src/logid/backend/raw/DeviceMonitor.cpp +++ b/src/logid/backend/raw/DeviceMonitor.cpp @@ -1,194 +1,194 @@ -/* - * Copyright 2019-2023 PixlOne - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * 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 - -extern "C" -{ -#include -} - -using namespace logid; -using namespace logid::backend::raw; - -DeviceMonitor::DeviceMonitor() : _io_monitor(std::make_shared()), - _ready(false) { - int ret; - _udev_context = udev_new(); - if (!_udev_context) - throw std::runtime_error("udev_new failed"); - - _udev_monitor = udev_monitor_new_from_netlink(_udev_context, - "udev"); - if (!_udev_monitor) { - if (_udev_context) - udev_unref(_udev_context); - throw std::runtime_error("udev_monitor_new_from_netlink failed"); - } - - ret = udev_monitor_filter_add_match_subsystem_devtype( - _udev_monitor, "hidraw", nullptr); - if (0 != ret) { - if (_udev_monitor) - udev_monitor_unref(_udev_monitor); - if (_udev_context) - udev_unref(_udev_context); - throw std::system_error( - -ret, std::system_category(), - "udev_monitor_filter_add_match_subsystem_devtype"); - } - - ret = udev_monitor_enable_receiving(_udev_monitor); - if (0 != ret) { - if (_udev_monitor) - udev_monitor_unref(_udev_monitor); - if (_udev_context) - udev_unref(_udev_context); - throw std::system_error(-ret, std::system_category(), - "udev_monitor_enable_receiving"); - } - - _fd = udev_monitor_get_fd(_udev_monitor); -} - -DeviceMonitor::~DeviceMonitor() { - if (_ready) - _io_monitor->remove(_fd); - - if (_udev_monitor) - udev_monitor_unref(_udev_monitor); - if (_udev_context) - udev_unref(_udev_context); -} - -void DeviceMonitor::ready() { - if (_ready) - return; - _ready = true; - - _io_monitor->add(_fd, { - [self_weak = _self]() { - if (auto self = self_weak.lock()) { - struct udev_device* device = udev_monitor_receive_device(self->_udev_monitor); - std::string action = udev_device_get_action(device); - std::string dev_node = udev_device_get_devnode(device); - - if (action == "add") - run_task([self_weak, dev_node]() { - if (auto self = self_weak.lock()) - self->_addHandler(dev_node); - }); - else if (action == "remove") - run_task([self_weak, dev_node]() { - if (auto self = self_weak.lock()) - self->_removeHandler(dev_node); - }); - - udev_device_unref(device); - } - }, - []() { - throw std::runtime_error("udev hangup"); - }, - []() { - throw std::runtime_error("udev error"); - } - }); -} - -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) { - const char* dev_node_cstr = udev_device_get_devnode(device); - if (dev_node_cstr) { - const std::string dev_node {dev_node_cstr}; - udev_device_unref(device); - - _addHandler(dev_node); - } else { - udev_device_unref(device); - } - } - } - - udev_enumerate_unref(udev_enum); -} - -void DeviceMonitor::_addHandler(const std::string& device, int tries) { - try { - auto supported_reports = backend::hidpp::getSupportedReports( - RawDevice::getReportDescriptor(device)); - if (supported_reports) - addDevice(device); - else - logPrintf(DEBUG, "Unsupported device %s ignored", device.c_str()); - } catch (backend::DeviceNotReady& e) { - if (tries == max_tries) { - logPrintf(WARN, "Failed to add device %s after %d tries. Treating as failure.", - device.c_str(), max_tries); - } else { - /* Do exponential backoff for 2^tries * backoff ms. */ - std::chrono::milliseconds wait((1 << tries) * ready_backoff); - logPrintf(DEBUG, "Failed to add device %s on try %d, backing off for %dms", - device.c_str(), tries + 1, wait.count()); - run_task_after([self_weak = _self, device, tries]() { - if (auto self = self_weak.lock()) - self->_addHandler(device, tries + 1); - }, wait); - } - } catch (std::exception& e) { - logPrintf(WARN, "Error adding device %s: %s", device.c_str(), e.what()); - } -} - -void DeviceMonitor::_removeHandler(const std::string& device) { - try { - removeDevice(device); - } catch (std::exception& e) { - logPrintf(WARN, "Error removing device %s: %s", - device.c_str(), e.what()); - } -} - -std::shared_ptr DeviceMonitor::ioMonitor() const { - return _io_monitor; -} +/* + * Copyright 2019-2023 PixlOne + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * 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 + +extern "C" +{ +#include +} + +using namespace logid; +using namespace logid::backend::raw; + +DeviceMonitor::DeviceMonitor() : _io_monitor(std::make_shared()), + _ready(false) { + int ret; + _udev_context = udev_new(); + if (!_udev_context) + throw std::runtime_error("udev_new failed"); + + _udev_monitor = udev_monitor_new_from_netlink(_udev_context, + "udev"); + if (!_udev_monitor) { + if (_udev_context) + udev_unref(_udev_context); + throw std::runtime_error("udev_monitor_new_from_netlink failed"); + } + + ret = udev_monitor_filter_add_match_subsystem_devtype( + _udev_monitor, "hidraw", nullptr); + if (0 != ret) { + if (_udev_monitor) + udev_monitor_unref(_udev_monitor); + if (_udev_context) + udev_unref(_udev_context); + throw std::system_error( + -ret, std::system_category(), + "udev_monitor_filter_add_match_subsystem_devtype"); + } + + ret = udev_monitor_enable_receiving(_udev_monitor); + if (0 != ret) { + if (_udev_monitor) + udev_monitor_unref(_udev_monitor); + if (_udev_context) + udev_unref(_udev_context); + throw std::system_error(-ret, std::system_category(), + "udev_monitor_enable_receiving"); + } + + _fd = udev_monitor_get_fd(_udev_monitor); +} + +DeviceMonitor::~DeviceMonitor() { + if (_ready) + _io_monitor->remove(_fd); + + if (_udev_monitor) + udev_monitor_unref(_udev_monitor); + if (_udev_context) + udev_unref(_udev_context); +} + +void DeviceMonitor::ready() { + if (_ready) + return; + _ready = true; + + _io_monitor->add(_fd, { + [self_weak = _self]() { + if (auto self = self_weak.lock()) { + struct udev_device* device = udev_monitor_receive_device(self->_udev_monitor); + std::string action = udev_device_get_action(device); + std::string dev_node = udev_device_get_devnode(device); + + if (action == "add") + run_task([self_weak, dev_node]() { + if (auto self = self_weak.lock()) + self->_addHandler(dev_node); + }); + else if (action == "remove") + run_task([self_weak, dev_node]() { + if (auto self = self_weak.lock()) + self->_removeHandler(dev_node); + }); + + udev_device_unref(device); + } + }, + []() { + throw std::runtime_error("udev hangup"); + }, + []() { + throw std::runtime_error("udev error"); + } + }); +} + +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) { + const char* dev_node_cstr = udev_device_get_devnode(device); + if (dev_node_cstr) { + const std::string dev_node {dev_node_cstr}; + udev_device_unref(device); + + _addHandler(dev_node); + } else { + udev_device_unref(device); + } + } + } + + udev_enumerate_unref(udev_enum); +} + +void DeviceMonitor::_addHandler(const std::string& device, int tries) { + try { + auto supported_reports = backend::hidpp::getSupportedReports( + RawDevice::getReportDescriptor(device)); + if (supported_reports) + addDevice(device); + else + logPrintf(DEBUG, "Unsupported device %s ignored", device.c_str()); + } catch (backend::DeviceNotReady& e) { + if (tries == max_tries) { + logPrintf(WARN, "Failed to add device %s after %d tries. Treating as failure.", + device.c_str(), max_tries); + } else { + /* Do exponential backoff for 2^tries * backoff ms. */ + std::chrono::milliseconds wait((1 << tries) * ready_backoff); + logPrintf(DEBUG, "Failed to add device %s on try %d, backing off for %dms", + device.c_str(), tries + 1, wait.count()); + run_task_after([self_weak = _self, device, tries]() { + if (auto self = self_weak.lock()) + self->_addHandler(device, tries + 1); + }, wait); + } + } catch (std::exception& e) { + logPrintf(WARN, "Error adding device %s: %s", device.c_str(), e.what()); + } +} + +void DeviceMonitor::_removeHandler(const std::string& device) { + try { + removeDevice(device); + } catch (std::exception& e) { + logPrintf(WARN, "Error removing device %s: %s", + device.c_str(), e.what()); + } +} + +std::shared_ptr DeviceMonitor::ioMonitor() const { + return _io_monitor; +} diff --git a/src/logid/backend/raw/DeviceMonitor.h b/src/logid/backend/raw/DeviceMonitor.h index 851a004..11557e4 100644 --- a/src/logid/backend/raw/DeviceMonitor.h +++ b/src/logid/backend/raw/DeviceMonitor.h @@ -1,101 +1,101 @@ -/* - * Copyright 2019-2023 PixlOne - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * 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 - -#include -#include -#include -#include - -extern "C" -{ -struct udev; -struct udev_monitor; -} - -namespace logid::backend::raw { - class IOMonitor; - - static constexpr int max_tries = 5; - static constexpr int ready_backoff = 500; - - template - class _deviceMonitorWrapper : public T { - friend class Device; - - public: - template - explicit _deviceMonitorWrapper(Args... args) : T(std::forward(args)...) {} - - template - static std::shared_ptr make(Args... args) { - return std::make_shared<_deviceMonitorWrapper>(std::forward(args)...); - } - }; - - class DeviceMonitor { - public: - virtual ~DeviceMonitor(); - - void enumerate(); - - [[nodiscard]] std::shared_ptr ioMonitor() const; - - template - static std::shared_ptr make(Args... args) { - auto device_monitor = _deviceMonitorWrapper::make(std::forward(args)...); - device_monitor->_self = device_monitor; - device_monitor->ready(); - - return device_monitor; - } - - protected: - DeviceMonitor(); - - // This should be run once the derived class is ready - void ready(); - - virtual void addDevice(std::string device) = 0; - - virtual void removeDevice(std::string device) = 0; - - template - [[nodiscard]] std::weak_ptr self() const { - return std::dynamic_pointer_cast(_self.lock()); - } - - private: - void _addHandler(const std::string& device, int tries = 0); - - void _removeHandler(const std::string& device); - - std::shared_ptr _io_monitor; - - struct udev* _udev_context; - struct udev_monitor* _udev_monitor; - int _fd; - bool _ready; - - std::weak_ptr _self; - }; -} - +/* + * Copyright 2019-2023 PixlOne + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * 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 + +#include +#include +#include +#include + +extern "C" +{ +struct udev; +struct udev_monitor; +} + +namespace logid::backend::raw { + class IOMonitor; + + static constexpr int max_tries = 5; + static constexpr int ready_backoff = 500; + + template + class _deviceMonitorWrapper : public T { + friend class Device; + + public: + template + explicit _deviceMonitorWrapper(Args... args) : T(std::forward(args)...) {} + + template + static std::shared_ptr make(Args... args) { + return std::make_shared<_deviceMonitorWrapper>(std::forward(args)...); + } + }; + + class DeviceMonitor { + public: + virtual ~DeviceMonitor(); + + void enumerate(); + + [[nodiscard]] std::shared_ptr ioMonitor() const; + + template + static std::shared_ptr make(Args... args) { + auto device_monitor = _deviceMonitorWrapper::make(std::forward(args)...); + device_monitor->_self = device_monitor; + device_monitor->ready(); + + return device_monitor; + } + + protected: + DeviceMonitor(); + + // This should be run once the derived class is ready + void ready(); + + virtual void addDevice(std::string device) = 0; + + virtual void removeDevice(std::string device) = 0; + + template + [[nodiscard]] std::weak_ptr self() const { + return std::dynamic_pointer_cast(_self.lock()); + } + + private: + void _addHandler(const std::string& device, int tries = 0); + + void _removeHandler(const std::string& device); + + std::shared_ptr _io_monitor; + + struct udev* _udev_context; + struct udev_monitor* _udev_monitor; + int _fd; + bool _ready; + + std::weak_ptr _self; + }; +} + #endif //LOGID_BACKEND_RAW_DEVICEMONITOR_H \ No newline at end of file diff --git a/src/logid/backend/raw/EventHandler.h b/src/logid/backend/raw/EventHandler.h index 2d136ec..106d832 100644 --- a/src/logid/backend/raw/EventHandler.h +++ b/src/logid/backend/raw/EventHandler.h @@ -1,38 +1,38 @@ -/* - * Copyright 2019-2023 PixlOne - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * 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 - -#include -#include -#include - -namespace logid::backend::raw { - struct RawEventHandler { - std::function&)> condition; - std::function&)> callback; - - RawEventHandler(std::function&)> cond, - std::function&)> call) : - condition(std::move(cond)), callback(std::move(call)) { - } - }; -} - +/* + * Copyright 2019-2023 PixlOne + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * 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 + +#include +#include +#include + +namespace logid::backend::raw { + struct RawEventHandler { + std::function&)> condition; + std::function&)> callback; + + RawEventHandler(std::function&)> cond, + std::function&)> call) : + condition(std::move(cond)), callback(std::move(call)) { + } + }; +} + #endif //LOGID_BACKEND_RAW_DEFS_H \ No newline at end of file diff --git a/src/logid/backend/raw/IOMonitor.cpp b/src/logid/backend/raw/IOMonitor.cpp index 7a7a323..5e3660a 100644 --- a/src/logid/backend/raw/IOMonitor.cpp +++ b/src/logid/backend/raw/IOMonitor.cpp @@ -1,167 +1,167 @@ -/* - * Copyright 2019-2023 PixlOne - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * 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 - -extern "C" -{ -#include -#include -#include -} - -using namespace logid::backend::raw; - -IOHandler::IOHandler(std::function r, - std::function hup, - std::function err) : - read(std::move(r)), - hangup(std::move(hup)), - error(std::move(err)) { -} - -IOMonitor::IOMonitor() : _epoll_fd(epoll_create1(0)), - _event_fd(eventfd(0, EFD_NONBLOCK)) { - if (_epoll_fd < 0) { - if (_event_fd >= 0) - close(_event_fd); - throw std::runtime_error("failed to create epoll fd"); - } - - if (_event_fd < 0) { - close(_epoll_fd); - throw std::runtime_error("failed to create event fd"); - } - - struct epoll_event event{}; - event.events = EPOLLIN; - event.data.fd = _event_fd; - - if (::epoll_ctl(_epoll_fd, EPOLL_CTL_ADD, _event_fd, &event)) { - throw std::system_error(errno, std::generic_category()); - } - - _fds.emplace(_event_fd, nullptr); - - _io_thread = std::make_unique([this]() { - _listen(); - }); -} - -IOMonitor::~IOMonitor() noexcept { - _stop(); - - if (_event_fd >= 0) - ::close(_event_fd); - - if (_epoll_fd >= 0) - ::close(_epoll_fd); -} - -void IOMonitor::_listen() { - std::unique_lock lock(_run_mutex); - std::vector events; - - if (_is_running) - throw std::runtime_error("IOMonitor double run"); - - _is_running = true; - - while (_is_running) { - if (events.size() != _fds.size()) - events.resize(_fds.size()); - - int ev_count = ::epoll_wait(_epoll_fd, events.data(), (int) events.size(), -1); - for (int i = 0; i < ev_count; ++i) { - std::shared_ptr handler; - - if (events[i].data.fd == _event_fd) { - if (events[i].events & EPOLLIN) { - lock.unlock(); - /* Wait until done yielding */ - const std::lock_guard yield_lock(_yield_mutex); - uint64_t event; - while (-1 != ::eventfd_read(_event_fd, &event)) { } - lock.lock(); - } - } else { - try { - handler = _fds.at(events[i].data.fd); - } catch (std::out_of_range& e) { - continue; - } - - lock.unlock(); - try { - if (events[i].events & EPOLLIN) - handler->read(); - if (events[i].events & EPOLLHUP) - handler->hangup(); - if (events[i].events & EPOLLERR) - handler->error(); - } catch (std::exception& e) { - logPrintf(ERROR, "Unhandled I/O handler error: %s", e.what()); - } - lock.lock(); - - } - } - } -} - -void IOMonitor::_stop() noexcept { - _is_running = false; - _yield(); - _io_thread->join(); -} - -std::unique_lock IOMonitor::_yield() noexcept { - /* Prevent listener thread from grabbing lock during yielding */ - std::unique_lock yield_lock(_yield_mutex); - - std::unique_lock run_lock(_run_mutex, std::try_to_lock); - if (!run_lock.owns_lock()) { - ::eventfd_write(_event_fd, 1); - run_lock = std::unique_lock(_run_mutex); - } - - return run_lock; -} - -void IOMonitor::add(int fd, IOHandler handler) { - const auto lock = _yield(); - - struct epoll_event event{}; - event.events = EPOLLIN | EPOLLHUP | EPOLLERR; - event.data.fd = fd; - - if (!_fds.contains(fd)) { - if (::epoll_ctl(_epoll_fd, EPOLL_CTL_ADD, fd, &event)) - throw std::system_error(errno, std::generic_category()); - _fds.emplace(fd, std::make_shared(std::move(handler))); - } else { - throw std::runtime_error("duplicate io fd"); - } -} - -void IOMonitor::remove(int fd) noexcept { - const auto lock = _yield(); - ::epoll_ctl(_epoll_fd, EPOLL_CTL_DEL, fd, nullptr); - _fds.erase(fd); +/* + * Copyright 2019-2023 PixlOne + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * 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 + +extern "C" +{ +#include +#include +#include +} + +using namespace logid::backend::raw; + +IOHandler::IOHandler(std::function r, + std::function hup, + std::function err) : + read(std::move(r)), + hangup(std::move(hup)), + error(std::move(err)) { +} + +IOMonitor::IOMonitor() : _epoll_fd(epoll_create1(0)), + _event_fd(eventfd(0, EFD_NONBLOCK)) { + if (_epoll_fd < 0) { + if (_event_fd >= 0) + close(_event_fd); + throw std::runtime_error("failed to create epoll fd"); + } + + if (_event_fd < 0) { + close(_epoll_fd); + throw std::runtime_error("failed to create event fd"); + } + + struct epoll_event event{}; + event.events = EPOLLIN; + event.data.fd = _event_fd; + + if (::epoll_ctl(_epoll_fd, EPOLL_CTL_ADD, _event_fd, &event)) { + throw std::system_error(errno, std::generic_category()); + } + + _fds.emplace(_event_fd, nullptr); + + _io_thread = std::make_unique([this]() { + _listen(); + }); +} + +IOMonitor::~IOMonitor() noexcept { + _stop(); + + if (_event_fd >= 0) + ::close(_event_fd); + + if (_epoll_fd >= 0) + ::close(_epoll_fd); +} + +void IOMonitor::_listen() { + std::unique_lock lock(_run_mutex); + std::vector events; + + if (_is_running) + throw std::runtime_error("IOMonitor double run"); + + _is_running = true; + + while (_is_running) { + if (events.size() != _fds.size()) + events.resize(_fds.size()); + + int ev_count = ::epoll_wait(_epoll_fd, events.data(), (int) events.size(), -1); + for (int i = 0; i < ev_count; ++i) { + std::shared_ptr handler; + + if (events[i].data.fd == _event_fd) { + if (events[i].events & EPOLLIN) { + lock.unlock(); + /* Wait until done yielding */ + const std::lock_guard yield_lock(_yield_mutex); + uint64_t event; + while (-1 != ::eventfd_read(_event_fd, &event)) { } + lock.lock(); + } + } else { + try { + handler = _fds.at(events[i].data.fd); + } catch (std::out_of_range& e) { + continue; + } + + lock.unlock(); + try { + if (events[i].events & EPOLLIN) + handler->read(); + if (events[i].events & EPOLLHUP) + handler->hangup(); + if (events[i].events & EPOLLERR) + handler->error(); + } catch (std::exception& e) { + logPrintf(ERROR, "Unhandled I/O handler error: %s", e.what()); + } + lock.lock(); + + } + } + } +} + +void IOMonitor::_stop() noexcept { + _is_running = false; + _yield(); + _io_thread->join(); +} + +std::unique_lock IOMonitor::_yield() noexcept { + /* Prevent listener thread from grabbing lock during yielding */ + std::unique_lock yield_lock(_yield_mutex); + + std::unique_lock run_lock(_run_mutex, std::try_to_lock); + if (!run_lock.owns_lock()) { + ::eventfd_write(_event_fd, 1); + run_lock = std::unique_lock(_run_mutex); + } + + return run_lock; +} + +void IOMonitor::add(int fd, IOHandler handler) { + const auto lock = _yield(); + + struct epoll_event event{}; + event.events = EPOLLIN | EPOLLHUP | EPOLLERR; + event.data.fd = fd; + + if (!_fds.contains(fd)) { + if (::epoll_ctl(_epoll_fd, EPOLL_CTL_ADD, fd, &event)) + throw std::system_error(errno, std::generic_category()); + _fds.emplace(fd, std::make_shared(std::move(handler))); + } else { + throw std::runtime_error("duplicate io fd"); + } +} + +void IOMonitor::remove(int fd) noexcept { + const auto lock = _yield(); + ::epoll_ctl(_epoll_fd, EPOLL_CTL_DEL, fd, nullptr); + _fds.erase(fd); } \ No newline at end of file diff --git a/src/logid/backend/raw/IOMonitor.h b/src/logid/backend/raw/IOMonitor.h index 83b4ff1..a9774c1 100644 --- a/src/logid/backend/raw/IOMonitor.h +++ b/src/logid/backend/raw/IOMonitor.h @@ -1,75 +1,75 @@ -/* - * Copyright 2019-2023 PixlOne - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * 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_IOMONITOR_H -#define LOGID_BACKEND_RAW_IOMONITOR_H - -#include -#include -#include -#include -#include -#include -#include - -namespace logid::backend::raw { - struct IOHandler { - std::function read; - std::function hangup; - std::function error; - - IOHandler(std::function r, - std::function hup, - std::function err); - }; - - class IOMonitor { - public: - IOMonitor(); - - IOMonitor(IOMonitor&&) = delete; - - IOMonitor(const IOMonitor&) = delete; - - IOMonitor& operator=(IOMonitor&&) = delete; - - IOMonitor& operator=(const IOMonitor&) = delete; - - ~IOMonitor() noexcept; - - void add(int fd, IOHandler handler); - - void remove(int fd) noexcept; - private: - void _listen(); // This is a blocking call - void _stop() noexcept; - std::unique_lock _yield() noexcept; - - std::unique_ptr _io_thread; - - std::mutex _run_mutex; - std::mutex _yield_mutex; - - std::map> _fds; - std::atomic_bool _is_running; - - const int _epoll_fd; - const int _event_fd; - }; -} - -#endif //LOGID_BACKEND_RAW_IOMONITOR_H +/* + * Copyright 2019-2023 PixlOne + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * 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_IOMONITOR_H +#define LOGID_BACKEND_RAW_IOMONITOR_H + +#include +#include +#include +#include +#include +#include +#include + +namespace logid::backend::raw { + struct IOHandler { + std::function read; + std::function hangup; + std::function error; + + IOHandler(std::function r, + std::function hup, + std::function err); + }; + + class IOMonitor { + public: + IOMonitor(); + + IOMonitor(IOMonitor&&) = delete; + + IOMonitor(const IOMonitor&) = delete; + + IOMonitor& operator=(IOMonitor&&) = delete; + + IOMonitor& operator=(const IOMonitor&) = delete; + + ~IOMonitor() noexcept; + + void add(int fd, IOHandler handler); + + void remove(int fd) noexcept; + private: + void _listen(); // This is a blocking call + void _stop() noexcept; + std::unique_lock _yield() noexcept; + + std::unique_ptr _io_thread; + + std::mutex _run_mutex; + std::mutex _yield_mutex; + + std::map> _fds; + std::atomic_bool _is_running; + + const int _epoll_fd; + const int _event_fd; + }; +} + +#endif //LOGID_BACKEND_RAW_IOMONITOR_H diff --git a/src/logid/backend/raw/RawDevice.cpp b/src/logid/backend/raw/RawDevice.cpp index 8647899..4df4430 100644 --- a/src/logid/backend/raw/RawDevice.cpp +++ b/src/logid/backend/raw/RawDevice.cpp @@ -1,248 +1,248 @@ -/* - * Copyright 2019-2023 PixlOne - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * 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 - -extern "C" -{ -#include -#include -#include -#include -#include -#include -} - -using namespace logid::backend::raw; -using namespace logid::backend; -using namespace std::chrono; - -static constexpr int max_write_tries = 8; - -static const std::regex virtual_path_regex(R"~((.*\/)(.*:)([0-9]+))~"); - -int get_fd(const std::string& path) { - int fd = ::open(path.c_str(), O_RDWR | O_NONBLOCK); - if (fd == -1) - throw std::system_error(errno, std::system_category(), - "RawDevice open failed"); - - return fd; -} - -RawDevice::dev_info get_dev_info(int fd) { - hidraw_devinfo dev_info{}; - if (-1 == ::ioctl(fd, HIDIOCGRAWINFO, &dev_info)) { - int err = errno; - ::close(fd); - throw std::system_error(err, std::system_category(), - "RawDevice HIDIOCGRAWINFO failed"); - } - - RawDevice::BusType type = RawDevice::OtherBus; - - switch (dev_info.bustype) { - case BUS_USB: - type = RawDevice::USB; - break; - case BUS_BLUETOOTH: - type = RawDevice::Bluetooth; - break; - default:; - } - - - return { - .vid = dev_info.vendor, - .pid = dev_info.product, - .bus_type = type - }; -} - -std::string get_phys(int fd) { - ssize_t len; - char buf[256]; - if (-1 == (len = ::ioctl(fd, HIDIOCGRAWPHYS(sizeof(buf)), buf))) { - int err = errno; - ::close(fd); - throw std::system_error(err, std::system_category(), - "RawDevice HIDIOCGRAWPHYS failed"); - } - - return {buf, static_cast(len) - 1}; -} - -std::string get_name(int fd) { - ssize_t len; - char name_buf[256]; - if (-1 == (len = ::ioctl(fd, HIDIOCGRAWNAME(sizeof(name_buf)), name_buf))) { - int err = errno; - ::close(fd); - throw std::system_error(err, std::system_category(), - "RawDevice HIDIOCGRAWNAME failed"); - } - return {name_buf, static_cast(len) - 1}; -} - -RawDevice::RawDevice(std::string path, const std::shared_ptr& monitor) : - _valid(true), _path(std::move(path)), _fd(get_fd(_path)), - _dev_info(get_dev_info(_fd)), _name(get_name(_fd)), - _report_desc(getReportDescriptor(_fd)), _io_monitor(monitor->ioMonitor()), - _event_handlers(std::make_shared>()) { - - if (busType() == USB) { - auto phys = get_phys(_fd); - _sub_device = std::regex_match(phys, virtual_path_regex); - } -} - -void RawDevice::_ready() { - _io_monitor->add(_fd, { - [self_weak = _self]() { - if (auto self = self_weak.lock()) - self->_readReports(); - }, - [self_weak = _self]() { - if (auto self = self_weak.lock()) - self->_valid = false; - }, - [self_weak = _self]() { - if (auto self = self_weak.lock()) - self->_valid = false; - } - }); -} - -RawDevice::~RawDevice() noexcept { - _io_monitor->remove(_fd); - ::close(_fd); -} - -const std::string& RawDevice::rawPath() const { - return _path; -} - -const std::string& RawDevice::name() const { - return _name; -} - -[[maybe_unused]] -int16_t RawDevice::vendorId() const { - return _dev_info.vid; -} - -int16_t RawDevice::productId() const { - return _dev_info.pid; -} - -RawDevice::BusType RawDevice::busType() const { - return _dev_info.bus_type; -} - -bool RawDevice::isSubDevice() const { - return _sub_device; -} - -std::vector RawDevice::getReportDescriptor(const std::string& path) { - int fd = ::open(path.c_str(), O_RDWR | O_NONBLOCK); - if (fd == -1) - throw std::system_error(errno, std::system_category(), - "open failed"); - - auto report_desc = getReportDescriptor(fd); - ::close(fd); - return report_desc; -} - -std::vector RawDevice::getReportDescriptor(int fd) { - hidraw_report_descriptor report_desc{}; - if (-1 == ::ioctl(fd, HIDIOCGRDESCSIZE, &report_desc.size)) { - int err = errno; - ::close(fd); - throw std::system_error(err, std::system_category(), - "RawDevice HIDIOCGRDESCSIZE failed"); - } - if (-1 == ::ioctl(fd, HIDIOCGRDESC, &report_desc)) { - int err = errno; - ::close(fd); - throw std::system_error(err, std::system_category(), - "RawDevice HIDIOCGRDESC failed"); - } - return {report_desc.value, report_desc.value + report_desc.size}; -} - -const std::vector& RawDevice::reportDescriptor() const { - return _report_desc; -} - -void RawDevice::sendReport(const std::vector& report) { - if (!_valid) { - // We could throw an error here, but this will likely be closed soon. - return; - } - - if (logid::global_loglevel <= LogLevel::RAWREPORT) { - printf("[RAWREPORT] %s OUT: ", _path.c_str()); - for (auto& i: report) - printf("%02x ", i); - printf("\n"); - } - - - for (int i = 0; i < max_write_tries && write(_fd, report.data(), report.size()) == -1; ++i) { - auto err = errno; - if (err != EPIPE) - throw std::system_error(err, std::system_category(), - "sendReport write failed"); - } -} - -EventHandlerLock RawDevice::addEventHandler(RawEventHandler handler) { - return {_event_handlers, _event_handlers->add(std::forward(handler))}; -} - -void RawDevice::_readReports() { - uint8_t buf[max_data_length]; - ssize_t len; - - while (-1 != (len = ::read(_fd, buf, max_data_length))) { - assert(len <= max_data_length); - std::vector report(buf, buf + len); - - if (logid::global_loglevel <= LogLevel::RAWREPORT) { - printf("[RAWREPORT] %s IN: ", _path.c_str()); - for (auto& i: report) - printf("%02x ", i); - printf("\n"); - } - - _handleEvent(report); - } -} - -void RawDevice::_handleEvent(const std::vector& report) { - _event_handlers->run_all(report); -} +/* + * Copyright 2019-2023 PixlOne + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * 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 + +extern "C" +{ +#include +#include +#include +#include +#include +#include +} + +using namespace logid::backend::raw; +using namespace logid::backend; +using namespace std::chrono; + +static constexpr int max_write_tries = 8; + +static const std::regex virtual_path_regex(R"~((.*\/)(.*:)([0-9]+))~"); + +int get_fd(const std::string& path) { + int fd = ::open(path.c_str(), O_RDWR | O_NONBLOCK); + if (fd == -1) + throw std::system_error(errno, std::system_category(), + "RawDevice open failed"); + + return fd; +} + +RawDevice::dev_info get_dev_info(int fd) { + hidraw_devinfo dev_info{}; + if (-1 == ::ioctl(fd, HIDIOCGRAWINFO, &dev_info)) { + int err = errno; + ::close(fd); + throw std::system_error(err, std::system_category(), + "RawDevice HIDIOCGRAWINFO failed"); + } + + RawDevice::BusType type = RawDevice::OtherBus; + + switch (dev_info.bustype) { + case BUS_USB: + type = RawDevice::USB; + break; + case BUS_BLUETOOTH: + type = RawDevice::Bluetooth; + break; + default:; + } + + + return { + .vid = dev_info.vendor, + .pid = dev_info.product, + .bus_type = type + }; +} + +std::string get_phys(int fd) { + ssize_t len; + char buf[256]; + if (-1 == (len = ::ioctl(fd, HIDIOCGRAWPHYS(sizeof(buf)), buf))) { + int err = errno; + ::close(fd); + throw std::system_error(err, std::system_category(), + "RawDevice HIDIOCGRAWPHYS failed"); + } + + return {buf, static_cast(len) - 1}; +} + +std::string get_name(int fd) { + ssize_t len; + char name_buf[256]; + if (-1 == (len = ::ioctl(fd, HIDIOCGRAWNAME(sizeof(name_buf)), name_buf))) { + int err = errno; + ::close(fd); + throw std::system_error(err, std::system_category(), + "RawDevice HIDIOCGRAWNAME failed"); + } + return {name_buf, static_cast(len) - 1}; +} + +RawDevice::RawDevice(std::string path, const std::shared_ptr& monitor) : + _valid(true), _path(std::move(path)), _fd(get_fd(_path)), + _dev_info(get_dev_info(_fd)), _name(get_name(_fd)), + _report_desc(getReportDescriptor(_fd)), _io_monitor(monitor->ioMonitor()), + _event_handlers(std::make_shared>()) { + + if (busType() == USB) { + auto phys = get_phys(_fd); + _sub_device = std::regex_match(phys, virtual_path_regex); + } +} + +void RawDevice::_ready() { + _io_monitor->add(_fd, { + [self_weak = _self]() { + if (auto self = self_weak.lock()) + self->_readReports(); + }, + [self_weak = _self]() { + if (auto self = self_weak.lock()) + self->_valid = false; + }, + [self_weak = _self]() { + if (auto self = self_weak.lock()) + self->_valid = false; + } + }); +} + +RawDevice::~RawDevice() noexcept { + _io_monitor->remove(_fd); + ::close(_fd); +} + +const std::string& RawDevice::rawPath() const { + return _path; +} + +const std::string& RawDevice::name() const { + return _name; +} + +[[maybe_unused]] +int16_t RawDevice::vendorId() const { + return _dev_info.vid; +} + +int16_t RawDevice::productId() const { + return _dev_info.pid; +} + +RawDevice::BusType RawDevice::busType() const { + return _dev_info.bus_type; +} + +bool RawDevice::isSubDevice() const { + return _sub_device; +} + +std::vector RawDevice::getReportDescriptor(const std::string& path) { + int fd = ::open(path.c_str(), O_RDWR | O_NONBLOCK); + if (fd == -1) + throw std::system_error(errno, std::system_category(), + "open failed"); + + auto report_desc = getReportDescriptor(fd); + ::close(fd); + return report_desc; +} + +std::vector RawDevice::getReportDescriptor(int fd) { + hidraw_report_descriptor report_desc{}; + if (-1 == ::ioctl(fd, HIDIOCGRDESCSIZE, &report_desc.size)) { + int err = errno; + ::close(fd); + throw std::system_error(err, std::system_category(), + "RawDevice HIDIOCGRDESCSIZE failed"); + } + if (-1 == ::ioctl(fd, HIDIOCGRDESC, &report_desc)) { + int err = errno; + ::close(fd); + throw std::system_error(err, std::system_category(), + "RawDevice HIDIOCGRDESC failed"); + } + return {report_desc.value, report_desc.value + report_desc.size}; +} + +const std::vector& RawDevice::reportDescriptor() const { + return _report_desc; +} + +void RawDevice::sendReport(const std::vector& report) { + if (!_valid) { + // We could throw an error here, but this will likely be closed soon. + return; + } + + if (logid::global_loglevel <= LogLevel::RAWREPORT) { + printf("[RAWREPORT] %s OUT: ", _path.c_str()); + for (auto& i: report) + printf("%02x ", i); + printf("\n"); + } + + + for (int i = 0; i < max_write_tries && write(_fd, report.data(), report.size()) == -1; ++i) { + auto err = errno; + if (err != EPIPE) + throw std::system_error(err, std::system_category(), + "sendReport write failed"); + } +} + +EventHandlerLock RawDevice::addEventHandler(RawEventHandler handler) { + return {_event_handlers, _event_handlers->add(std::forward(handler))}; +} + +void RawDevice::_readReports() { + uint8_t buf[max_data_length]; + ssize_t len; + + while (-1 != (len = ::read(_fd, buf, max_data_length))) { + assert(len <= max_data_length); + std::vector report(buf, buf + len); + + if (logid::global_loglevel <= LogLevel::RAWREPORT) { + printf("[RAWREPORT] %s IN: ", _path.c_str()); + for (auto& i: report) + printf("%02x ", i); + printf("\n"); + } + + _handleEvent(report); + } +} + +void RawDevice::_handleEvent(const std::vector& report) { + _event_handlers->run_all(report); +} diff --git a/src/logid/backend/raw/RawDevice.h b/src/logid/backend/raw/RawDevice.h index 7e93a30..db877f7 100644 --- a/src/logid/backend/raw/RawDevice.h +++ b/src/logid/backend/raw/RawDevice.h @@ -1,124 +1,124 @@ -/* - * Copyright 2019-2023 PixlOne - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * 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 - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace logid::backend::raw { - class DeviceMonitor; - - class IOMonitor; - - template - class RawDeviceWrapper : public T { - public: - template - RawDeviceWrapper(Args... args) : T(std::forward(args)...) { } - }; - - class RawDevice { - template - friend class RawDeviceWrapper; - public: - static constexpr int max_data_length = 32; - typedef RawEventHandler EventHandler; - - enum BusType { - USB, - Bluetooth, - OtherBus - }; - - struct dev_info { - int16_t vid; - int16_t pid; - BusType bus_type; - }; - - template - static std::shared_ptr make(Args... args) { - auto raw_dev = std::make_shared>( - std::forward(args)...); - raw_dev->_self = raw_dev; - raw_dev->_ready(); - return raw_dev; - } - - ~RawDevice() noexcept; - - [[nodiscard]] const std::string& rawPath() const; - - [[nodiscard]] const std::string& name() const; - - [[maybe_unused]] - [[nodiscard]] int16_t vendorId() const; - - [[nodiscard]] int16_t productId() const; - - [[nodiscard]] BusType busType() const; - - [[nodiscard]] bool isSubDevice() const; - - static std::vector getReportDescriptor(const std::string& path); - - static std::vector getReportDescriptor(int fd); - - [[nodiscard]] const std::vector& reportDescriptor() const; - - void sendReport(const std::vector& report); - - [[nodiscard]] EventHandlerLock addEventHandler(RawEventHandler handler); - - private: - RawDevice(std::string path, const std::shared_ptr& monitor); - - void _ready(); - - void _readReports(); - - std::atomic_bool _valid; - - const std::string _path; - const int _fd; - const dev_info _dev_info; - const std::string _name; - const std::vector _report_desc; - - std::shared_ptr _io_monitor; - - std::weak_ptr _self; - - bool _sub_device = false; - - std::shared_ptr> _event_handlers; - - void _handleEvent(const std::vector& report); - }; -} - -#endif //LOGID_BACKEND_RAWDEVICE_H +/* + * Copyright 2019-2023 PixlOne + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * 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 + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace logid::backend::raw { + class DeviceMonitor; + + class IOMonitor; + + template + class RawDeviceWrapper : public T { + public: + template + RawDeviceWrapper(Args... args) : T(std::forward(args)...) { } + }; + + class RawDevice { + template + friend class RawDeviceWrapper; + public: + static constexpr int max_data_length = 32; + typedef RawEventHandler EventHandler; + + enum BusType { + USB, + Bluetooth, + OtherBus + }; + + struct dev_info { + int16_t vid; + int16_t pid; + BusType bus_type; + }; + + template + static std::shared_ptr make(Args... args) { + auto raw_dev = std::make_shared>( + std::forward(args)...); + raw_dev->_self = raw_dev; + raw_dev->_ready(); + return raw_dev; + } + + ~RawDevice() noexcept; + + [[nodiscard]] const std::string& rawPath() const; + + [[nodiscard]] const std::string& name() const; + + [[maybe_unused]] + [[nodiscard]] int16_t vendorId() const; + + [[nodiscard]] int16_t productId() const; + + [[nodiscard]] BusType busType() const; + + [[nodiscard]] bool isSubDevice() const; + + static std::vector getReportDescriptor(const std::string& path); + + static std::vector getReportDescriptor(int fd); + + [[nodiscard]] const std::vector& reportDescriptor() const; + + void sendReport(const std::vector& report); + + [[nodiscard]] EventHandlerLock addEventHandler(RawEventHandler handler); + + private: + RawDevice(std::string path, const std::shared_ptr& monitor); + + void _ready(); + + void _readReports(); + + std::atomic_bool _valid; + + const std::string _path; + const int _fd; + const dev_info _dev_info; + const std::string _name; + const std::vector _report_desc; + + std::shared_ptr _io_monitor; + + std::weak_ptr _self; + + bool _sub_device = false; + + std::shared_ptr> _event_handlers; + + void _handleEvent(const std::vector& report); + }; +} + +#endif //LOGID_BACKEND_RAWDEVICE_H diff --git a/src/logid/config/config.cpp b/src/logid/config/config.cpp index 1e4f130..236e611 100644 --- a/src/logid/config/config.cpp +++ b/src/logid/config/config.cpp @@ -1,30 +1,30 @@ -/* - * Copyright 2019-2023 PixlOne - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * 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 - -using namespace logid; - -const char config::keys::name[] = "name"; -const char config::keys::cid[] = "cid"; -const char config::keys::direction[] = "direction"; - -void config::logError(const libconfig::Setting& setting, std::exception& e) { - logPrintf(WARN, "Error at line %d: %s", setting.getSourceLine(), e.what()); +/* + * Copyright 2019-2023 PixlOne + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * 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 + +using namespace logid; + +const char config::keys::name[] = "name"; +const char config::keys::cid[] = "cid"; +const char config::keys::direction[] = "direction"; + +void config::logError(const libconfig::Setting& setting, std::exception& e) { + logPrintf(WARN, "Error at line %d: %s", setting.getSourceLine(), e.what()); } \ No newline at end of file diff --git a/src/logid/config/group.h b/src/logid/config/group.h index 45e717b..3b6c7ca 100644 --- a/src/logid/config/group.h +++ b/src/logid/config/group.h @@ -1,203 +1,203 @@ -/* - * Copyright 2022 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_CONFIG_GROUP_H -#define LOGID_CONFIG_GROUP_H - -#include -#include -#include -#include -#include - -namespace logid::config { - template - void set(libconfig::Setting& parent, - const std::string& name, - const T& t); - - template - void set(libconfig::Setting& parent, const T& t); - - template - auto get(const libconfig::Setting& parent, const std::string& name); - - template - void append(libconfig::Setting& list, const T& t); - - template - struct group_io { - }; - - template - struct group_io { - static void get(const libconfig::Setting&, T*, - const std::vector&, const std::size_t) {} - - static void set(libconfig::Setting&, const T*, - const std::vector&, const std::size_t) {} - }; - - template - struct group_io { - static void get(const libconfig::Setting& s, T* t, - const std::vector& names, - const std::size_t index, A T::* arg, M T::*... rest) { - auto& x = t->*(arg); - A old{x}; - try { - x = config::get(s, names[index]); - group_io::get(s, t, names, index + 1, rest...); - } catch (libconfig::SettingTypeException& e) { - x = old; - throw; - } catch (libconfig::SettingException& e) { - x = old; - throw libconfig::SettingTypeException(s); - } - } - - static void set(libconfig::Setting& s, const T* t, - const std::vector& names, - const std::size_t index, A T::* arg, M T::*... rest) { - config::set(s, names[index], t->*(arg)); - group_io::set(s, t, names, index + 1, rest...); - } - }; - - template - struct signed_group; - - struct group { - private: - const std::vector _names; - const std::function&)> _getter; - const std::function&)> _setter; - - template - friend - struct signed_group; - protected: - template - explicit group(const std::array& names, - M T::*... args) : - _names(names.begin(), names.end()), - _getter([args...](const libconfig::Setting& s, group* g, - const std::vector& names) { - T* t = dynamic_cast(g); - group_io::get(s, t, names, 0, args...); - }), - _setter([args...](libconfig::Setting& s, const group* g, - const std::vector& names) { - const T* t = dynamic_cast(g); - group_io::set(s, t, names, 0, args...); - }) { - static_assert(std::is_base_of::value); - } - - group() : _getter([](const libconfig::Setting&, group*, - const std::vector&) {}), - _setter([](libconfig::Setting&, const group*, - const std::vector&) {}) {} - - public: - group(const group& o) = default; - - group(group&& o) noexcept = default; - - group& operator=(const group&) { - return *this; - } - - group& operator=(group&&) noexcept { - return *this; - } - - virtual ~group() = default; - - virtual void _save(libconfig::Setting& setting) const { - _setter(setting, this, _names); - } - - virtual void _load(const libconfig::Setting& setting) { - _getter(setting, this, _names); - } - }; - - template - struct normalize_signature { - static const T& make(const T& ret) { return ret; } - }; - - template<> - struct normalize_signature { - static std::string make(const std::string& data) { - std::string ret = data; - std::transform(ret.begin(), ret.end(), ret.begin(), ::tolower); - return ret; - } - }; - - template - struct signed_group : public group { - private: - const std::string _sig_field; - const Sign _signature; - protected: - signed_group(std::string sign_name, const Sign& sign_data) : - group(), _sig_field(std::move(sign_name)), - _signature(normalize_signature::make(sign_data)) {} - - template - signed_group( - std::string sign_name, const Sign& sign_data, - const std::array& names, - M T::*... args) : group(names, args...), - _sig_field(std::move(sign_name)), - _signature(normalize_signature::make(sign_data)) {} - - public: - signed_group(const signed_group& o) = default; - - signed_group(signed_group&& o) noexcept = default; - - signed_group& operator=(const signed_group&) { - return *this; - } - - signed_group& operator=(signed_group&&) noexcept { - return *this; - } - - void _save(libconfig::Setting& setting) const override { - set(setting, _sig_field, _signature); - _setter(setting, this, _names); - } - - void _load(const libconfig::Setting& setting) override { - if (normalize_signature::make(get(setting, _sig_field)) - != _signature) - throw libconfig::SettingTypeException(setting); - _getter(setting, this, _names); - } - }; - -} - -#endif //LOGID_CONFIG_GROUP_H +/* + * Copyright 2022 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_CONFIG_GROUP_H +#define LOGID_CONFIG_GROUP_H + +#include +#include +#include +#include +#include + +namespace logid::config { + template + void set(libconfig::Setting& parent, + const std::string& name, + const T& t); + + template + void set(libconfig::Setting& parent, const T& t); + + template + auto get(const libconfig::Setting& parent, const std::string& name); + + template + void append(libconfig::Setting& list, const T& t); + + template + struct group_io { + }; + + template + struct group_io { + static void get(const libconfig::Setting&, T*, + const std::vector&, const std::size_t) {} + + static void set(libconfig::Setting&, const T*, + const std::vector&, const std::size_t) {} + }; + + template + struct group_io { + static void get(const libconfig::Setting& s, T* t, + const std::vector& names, + const std::size_t index, A T::* arg, M T::*... rest) { + auto& x = t->*(arg); + A old{x}; + try { + x = config::get(s, names[index]); + group_io::get(s, t, names, index + 1, rest...); + } catch (libconfig::SettingTypeException& e) { + x = old; + throw; + } catch (libconfig::SettingException& e) { + x = old; + throw libconfig::SettingTypeException(s); + } + } + + static void set(libconfig::Setting& s, const T* t, + const std::vector& names, + const std::size_t index, A T::* arg, M T::*... rest) { + config::set(s, names[index], t->*(arg)); + group_io::set(s, t, names, index + 1, rest...); + } + }; + + template + struct signed_group; + + struct group { + private: + const std::vector _names; + const std::function&)> _getter; + const std::function&)> _setter; + + template + friend + struct signed_group; + protected: + template + explicit group(const std::array& names, + M T::*... args) : + _names(names.begin(), names.end()), + _getter([args...](const libconfig::Setting& s, group* g, + const std::vector& names) { + T* t = dynamic_cast(g); + group_io::get(s, t, names, 0, args...); + }), + _setter([args...](libconfig::Setting& s, const group* g, + const std::vector& names) { + const T* t = dynamic_cast(g); + group_io::set(s, t, names, 0, args...); + }) { + static_assert(std::is_base_of::value); + } + + group() : _getter([](const libconfig::Setting&, group*, + const std::vector&) {}), + _setter([](libconfig::Setting&, const group*, + const std::vector&) {}) {} + + public: + group(const group& o) = default; + + group(group&& o) noexcept = default; + + group& operator=(const group&) { + return *this; + } + + group& operator=(group&&) noexcept { + return *this; + } + + virtual ~group() = default; + + virtual void _save(libconfig::Setting& setting) const { + _setter(setting, this, _names); + } + + virtual void _load(const libconfig::Setting& setting) { + _getter(setting, this, _names); + } + }; + + template + struct normalize_signature { + static const T& make(const T& ret) { return ret; } + }; + + template<> + struct normalize_signature { + static std::string make(const std::string& data) { + std::string ret = data; + std::transform(ret.begin(), ret.end(), ret.begin(), ::tolower); + return ret; + } + }; + + template + struct signed_group : public group { + private: + const std::string _sig_field; + const Sign _signature; + protected: + signed_group(std::string sign_name, const Sign& sign_data) : + group(), _sig_field(std::move(sign_name)), + _signature(normalize_signature::make(sign_data)) {} + + template + signed_group( + std::string sign_name, const Sign& sign_data, + const std::array& names, + M T::*... args) : group(names, args...), + _sig_field(std::move(sign_name)), + _signature(normalize_signature::make(sign_data)) {} + + public: + signed_group(const signed_group& o) = default; + + signed_group(signed_group&& o) noexcept = default; + + signed_group& operator=(const signed_group&) { + return *this; + } + + signed_group& operator=(signed_group&&) noexcept { + return *this; + } + + void _save(libconfig::Setting& setting) const override { + set(setting, _sig_field, _signature); + _setter(setting, this, _names); + } + + void _load(const libconfig::Setting& setting) override { + if (normalize_signature::make(get(setting, _sig_field)) + != _signature) + throw libconfig::SettingTypeException(setting); + _getter(setting, this, _names); + } + }; + +} + +#endif //LOGID_CONFIG_GROUP_H diff --git a/src/logid/config/map.h b/src/logid/config/map.h index 13d1751..eab6889 100644 --- a/src/logid/config/map.h +++ b/src/logid/config/map.h @@ -1,60 +1,60 @@ -/* - * Copyright 2022 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_CONFIG_MAP_H -#define LOGID_CONFIG_MAP_H - -#include -#include -#include -#include - -namespace logid::config { - struct string_literal { }; - - template - struct string_literal_of : public string_literal { - constexpr static const char* value = str; - }; - - template - struct less_caseless { - constexpr bool operator()(const T& a, const T& b) const noexcept { - auto a_it = a.begin(), b_it = b.begin(); - for (; a_it != a.end() && b_it != b.end(); ++a_it, ++b_it) { - if (tolower(*a_it) != tolower(*b_it)) - return tolower(*a_it) < tolower(*b_it); - } - return b_it != b.end(); - } - }; - - // Warning: map must be a variant of groups or a group - template::key_compare, - typename Allocator=typename std::map::allocator_type> - class map : public std::map { - static_assert(std::is_base_of::value, - "KeyName must be a string_literal"); - public: - template - explicit map(Args... args) : - std::map(std::forward(args)...) {} - }; -} - -#endif //LOGID_CONFIG_MAP_H +/* + * Copyright 2022 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_CONFIG_MAP_H +#define LOGID_CONFIG_MAP_H + +#include +#include +#include +#include + +namespace logid::config { + struct string_literal { }; + + template + struct string_literal_of : public string_literal { + constexpr static const char* value = str; + }; + + template + struct less_caseless { + constexpr bool operator()(const T& a, const T& b) const noexcept { + auto a_it = a.begin(), b_it = b.begin(); + for (; a_it != a.end() && b_it != b.end(); ++a_it, ++b_it) { + if (tolower(*a_it) != tolower(*b_it)) + return tolower(*a_it) < tolower(*b_it); + } + return b_it != b.end(); + } + }; + + // Warning: map must be a variant of groups or a group + template::key_compare, + typename Allocator=typename std::map::allocator_type> + class map : public std::map { + static_assert(std::is_base_of::value, + "KeyName must be a string_literal"); + public: + template + explicit map(Args... args) : + std::map(std::forward(args)...) {} + }; +} + +#endif //LOGID_CONFIG_MAP_H diff --git a/src/logid/config/schema.h b/src/logid/config/schema.h index 6f16a8a..bb1813c 100644 --- a/src/logid/config/schema.h +++ b/src/logid/config/schema.h @@ -1,335 +1,335 @@ -/* - * Copyright 2022 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_CONFIG_SCHEMA_H -#define LOGID_CONFIG_SCHEMA_H - -#include - -namespace logid::actions { - class ChangeDPI; - - class ChangeHostAction; - - class ChangeProfile; - - class CycleDPI; - - class GestureAction; - - class KeypressAction; - - class NullAction; - - class ToggleHiresScroll; - - class ToggleSmartShift; - - class AxisGesture; - - class IntervalGesture; - - class NullGesture; - - class ReleaseGesture; - - class ThresholdGesture; -} - -namespace logid::config { - struct keys { - static const char name[]; - static const char cid[]; - static const char direction[]; - }; - - struct NoAction : public signed_group { - typedef actions::NullAction action; - - NoAction() : signed_group("type", "None") {} - }; - - struct KeypressAction : public signed_group { - typedef actions::KeypressAction action; - std::optional< - std::variant>>> keys; - - KeypressAction() : signed_group( - "type", "Keypress", - {"keys"}, &KeypressAction::keys) { - } - }; - - struct ToggleSmartShift : public signed_group { - typedef actions::ToggleSmartShift action; - - ToggleSmartShift() : - signed_group("type", "ToggleSmartShift") {} - }; - - struct ToggleHiresScroll : public signed_group { - typedef actions::ToggleHiresScroll action; - - ToggleHiresScroll() : - signed_group("type", "ToggleHiresScroll") {} - }; - - struct CycleDPI : public signed_group { - typedef actions::CycleDPI action; - std::optional> dpis; - std::optional sensor; - - CycleDPI() : signed_group( - "type", "CycleDPI", - {"dpis", "sensor"}, - &CycleDPI::dpis, - &CycleDPI::sensor) {} - }; - - struct ChangeDPI : public signed_group { - typedef actions::ChangeDPI action; - std::optional inc; - std::optional sensor; - - ChangeDPI() : signed_group( - "type", "ChangeDPI", - {"inc", "sensor"}, - &ChangeDPI::inc, - &ChangeDPI::sensor) {} - }; - - struct ChangeHost : public signed_group { - typedef actions::ChangeHostAction action; - std::optional> host; - - ChangeHost() : signed_group( - "type", "ChangeHost", - {"host"}, &ChangeHost::host) {} - }; - - struct ChangeProfile : public signed_group { - typedef actions::ChangeProfile action; - std::optional profile; - - ChangeProfile() : signed_group("type", "ChangeProfile", - {"profile"}, &ChangeProfile::profile) {} - }; - - typedef std::variant< - NoAction, - KeypressAction, - ToggleSmartShift, - ToggleHiresScroll, - CycleDPI, - ChangeDPI, - ChangeHost, - ChangeProfile - > BasicAction; - - struct AxisGesture : public signed_group { - typedef actions::AxisGesture gesture; - std::optional threshold; - std::optional> axis; - std::optional axis_multiplier; - - AxisGesture() : signed_group("mode", "Axis", - {"threshold", "axis", "axis_multiplier"}, - &AxisGesture::threshold, - &AxisGesture::axis, - &AxisGesture::axis_multiplier) {} - }; - - struct IntervalGesture : public signed_group { - typedef actions::IntervalGesture gesture; - std::optional threshold; - std::optional action; - std::optional interval; - protected: - explicit IntervalGesture(const std::string& name) : signed_group( - "mode", name, - {"threshold", "action", "interval"}, - &IntervalGesture::threshold, - &IntervalGesture::action, - &IntervalGesture::interval) {} - - public: - IntervalGesture() : IntervalGesture("OnInterval") {} - }; - - struct FewPixelsGesture : public IntervalGesture { - FewPixelsGesture() : IntervalGesture("OnFewPixels") {} - }; - - struct ReleaseGesture : public signed_group { - typedef actions::ReleaseGesture gesture; - std::optional threshold; - std::optional action; - - ReleaseGesture() : signed_group("mode", "OnRelease", - {"threshold", "action"}, - &ReleaseGesture::threshold, - &ReleaseGesture::action) {} - }; - - struct ThresholdGesture : public signed_group { - typedef actions::ThresholdGesture gesture; - std::optional threshold; - std::optional action; - - ThresholdGesture() : signed_group("mode", "OnThreshold", - {"threshold", "action"}, - &ThresholdGesture::threshold, - &ThresholdGesture::action) {} - }; - - struct NoGesture : public signed_group { - typedef actions::NullGesture gesture; - std::optional threshold; - - NoGesture() : signed_group("mode", "NoPress", - {"threshold"}, - &NoGesture::threshold) {} - }; - - typedef std::variant< - NoGesture, - AxisGesture, - IntervalGesture, - FewPixelsGesture, - ReleaseGesture, - ThresholdGesture - > Gesture; - - - struct GestureAction : public signed_group { - typedef actions::GestureAction action; - std::optional, - less_caseless>> gestures; - - GestureAction() : signed_group( - "type", "Gestures", - {"gestures"}, - &GestureAction::gestures) {} - }; - - typedef std::variant< - NoAction, - KeypressAction, - ToggleSmartShift, - ToggleHiresScroll, - CycleDPI, - ChangeDPI, - ChangeHost, - ChangeProfile, - GestureAction - > Action; - - struct Button : public group { - std::optional action; - - Button() : group({"action"}, - &Button::action) {} - }; - - struct SmartShift : public group { - std::optional on; - std::optional threshold; - std::optional torque; - - SmartShift() : group({"on", "threshold", "torque"}, - &SmartShift::on, &SmartShift::threshold, &SmartShift::torque) {} - }; - - - struct HiresScroll : public group { - std::optional hires; - std::optional invert; - std::optional target; - std::optional up; - std::optional down; - - HiresScroll() : group({"hires", "invert", "target", "up", "down"}, - &HiresScroll::hires, - &HiresScroll::invert, - &HiresScroll::target, - &HiresScroll::up, - &HiresScroll::down) {} - }; - - typedef std::variant> DPI; - - struct ThumbWheel : public group { - std::optional divert; - std::optional invert; - std::optional left; - std::optional right; - std::optional proxy; - std::optional touch; - std::optional tap; - - ThumbWheel() : group({"divert", "invert", "left", "right", - "proxy", "touch", "tap"}, - &ThumbWheel::divert, &ThumbWheel::invert, - &ThumbWheel::left, &ThumbWheel::right, - &ThumbWheel::proxy, &ThumbWheel::touch, - &ThumbWheel::tap) {} - }; - - typedef map> RemapButton; - - struct Profile : public group { - std::optional dpi; - std::optional smartshift; - std::optional> hiresscroll; - std::optional thumbwheel; - std::optional buttons; - - Profile() : group({"dpi", "smartshift", "hiresscroll", - "buttons", "thumbwheel"}, - &Profile::dpi, &Profile::smartshift, - &Profile::hiresscroll, &Profile::buttons, - &Profile::thumbwheel) {} - }; - - struct Device : public group { - ipcgull::property default_profile; - map> profiles; - - Device() : group({"default_profile", "profiles"}, - &Device::default_profile, - &Device::profiles), - default_profile(ipcgull::property_full_permissions, "") { - } - }; - - struct Config : public group { - std::optional, string_literal_of>> devices; - std::optional> ignore; - std::optional io_timeout; - std::optional workers; - - Config() : group({"devices", "ignore", "io_timeout", "workers"}, - &Config::devices, - &Config::ignore, - &Config::io_timeout, - &Config::workers) {} - }; -} - -#endif //LOGID_CONFIG_SCHEMA_H +/* + * Copyright 2022 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_CONFIG_SCHEMA_H +#define LOGID_CONFIG_SCHEMA_H + +#include + +namespace logid::actions { + class ChangeDPI; + + class ChangeHostAction; + + class ChangeProfile; + + class CycleDPI; + + class GestureAction; + + class KeypressAction; + + class NullAction; + + class ToggleHiresScroll; + + class ToggleSmartShift; + + class AxisGesture; + + class IntervalGesture; + + class NullGesture; + + class ReleaseGesture; + + class ThresholdGesture; +} + +namespace logid::config { + struct keys { + static const char name[]; + static const char cid[]; + static const char direction[]; + }; + + struct NoAction : public signed_group { + typedef actions::NullAction action; + + NoAction() : signed_group("type", "None") {} + }; + + struct KeypressAction : public signed_group { + typedef actions::KeypressAction action; + std::optional< + std::variant>>> keys; + + KeypressAction() : signed_group( + "type", "Keypress", + {"keys"}, &KeypressAction::keys) { + } + }; + + struct ToggleSmartShift : public signed_group { + typedef actions::ToggleSmartShift action; + + ToggleSmartShift() : + signed_group("type", "ToggleSmartShift") {} + }; + + struct ToggleHiresScroll : public signed_group { + typedef actions::ToggleHiresScroll action; + + ToggleHiresScroll() : + signed_group("type", "ToggleHiresScroll") {} + }; + + struct CycleDPI : public signed_group { + typedef actions::CycleDPI action; + std::optional> dpis; + std::optional sensor; + + CycleDPI() : signed_group( + "type", "CycleDPI", + {"dpis", "sensor"}, + &CycleDPI::dpis, + &CycleDPI::sensor) {} + }; + + struct ChangeDPI : public signed_group { + typedef actions::ChangeDPI action; + std::optional inc; + std::optional sensor; + + ChangeDPI() : signed_group( + "type", "ChangeDPI", + {"inc", "sensor"}, + &ChangeDPI::inc, + &ChangeDPI::sensor) {} + }; + + struct ChangeHost : public signed_group { + typedef actions::ChangeHostAction action; + std::optional> host; + + ChangeHost() : signed_group( + "type", "ChangeHost", + {"host"}, &ChangeHost::host) {} + }; + + struct ChangeProfile : public signed_group { + typedef actions::ChangeProfile action; + std::optional profile; + + ChangeProfile() : signed_group("type", "ChangeProfile", + {"profile"}, &ChangeProfile::profile) {} + }; + + typedef std::variant< + NoAction, + KeypressAction, + ToggleSmartShift, + ToggleHiresScroll, + CycleDPI, + ChangeDPI, + ChangeHost, + ChangeProfile + > BasicAction; + + struct AxisGesture : public signed_group { + typedef actions::AxisGesture gesture; + std::optional threshold; + std::optional> axis; + std::optional axis_multiplier; + + AxisGesture() : signed_group("mode", "Axis", + {"threshold", "axis", "axis_multiplier"}, + &AxisGesture::threshold, + &AxisGesture::axis, + &AxisGesture::axis_multiplier) {} + }; + + struct IntervalGesture : public signed_group { + typedef actions::IntervalGesture gesture; + std::optional threshold; + std::optional action; + std::optional interval; + protected: + explicit IntervalGesture(const std::string& name) : signed_group( + "mode", name, + {"threshold", "action", "interval"}, + &IntervalGesture::threshold, + &IntervalGesture::action, + &IntervalGesture::interval) {} + + public: + IntervalGesture() : IntervalGesture("OnInterval") {} + }; + + struct FewPixelsGesture : public IntervalGesture { + FewPixelsGesture() : IntervalGesture("OnFewPixels") {} + }; + + struct ReleaseGesture : public signed_group { + typedef actions::ReleaseGesture gesture; + std::optional threshold; + std::optional action; + + ReleaseGesture() : signed_group("mode", "OnRelease", + {"threshold", "action"}, + &ReleaseGesture::threshold, + &ReleaseGesture::action) {} + }; + + struct ThresholdGesture : public signed_group { + typedef actions::ThresholdGesture gesture; + std::optional threshold; + std::optional action; + + ThresholdGesture() : signed_group("mode", "OnThreshold", + {"threshold", "action"}, + &ThresholdGesture::threshold, + &ThresholdGesture::action) {} + }; + + struct NoGesture : public signed_group { + typedef actions::NullGesture gesture; + std::optional threshold; + + NoGesture() : signed_group("mode", "NoPress", + {"threshold"}, + &NoGesture::threshold) {} + }; + + typedef std::variant< + NoGesture, + AxisGesture, + IntervalGesture, + FewPixelsGesture, + ReleaseGesture, + ThresholdGesture + > Gesture; + + + struct GestureAction : public signed_group { + typedef actions::GestureAction action; + std::optional, + less_caseless>> gestures; + + GestureAction() : signed_group( + "type", "Gestures", + {"gestures"}, + &GestureAction::gestures) {} + }; + + typedef std::variant< + NoAction, + KeypressAction, + ToggleSmartShift, + ToggleHiresScroll, + CycleDPI, + ChangeDPI, + ChangeHost, + ChangeProfile, + GestureAction + > Action; + + struct Button : public group { + std::optional action; + + Button() : group({"action"}, + &Button::action) {} + }; + + struct SmartShift : public group { + std::optional on; + std::optional threshold; + std::optional torque; + + SmartShift() : group({"on", "threshold", "torque"}, + &SmartShift::on, &SmartShift::threshold, &SmartShift::torque) {} + }; + + + struct HiresScroll : public group { + std::optional hires; + std::optional invert; + std::optional target; + std::optional up; + std::optional down; + + HiresScroll() : group({"hires", "invert", "target", "up", "down"}, + &HiresScroll::hires, + &HiresScroll::invert, + &HiresScroll::target, + &HiresScroll::up, + &HiresScroll::down) {} + }; + + typedef std::variant> DPI; + + struct ThumbWheel : public group { + std::optional divert; + std::optional invert; + std::optional left; + std::optional right; + std::optional proxy; + std::optional touch; + std::optional tap; + + ThumbWheel() : group({"divert", "invert", "left", "right", + "proxy", "touch", "tap"}, + &ThumbWheel::divert, &ThumbWheel::invert, + &ThumbWheel::left, &ThumbWheel::right, + &ThumbWheel::proxy, &ThumbWheel::touch, + &ThumbWheel::tap) {} + }; + + typedef map> RemapButton; + + struct Profile : public group { + std::optional dpi; + std::optional smartshift; + std::optional> hiresscroll; + std::optional thumbwheel; + std::optional buttons; + + Profile() : group({"dpi", "smartshift", "hiresscroll", + "buttons", "thumbwheel"}, + &Profile::dpi, &Profile::smartshift, + &Profile::hiresscroll, &Profile::buttons, + &Profile::thumbwheel) {} + }; + + struct Device : public group { + ipcgull::property default_profile; + map> profiles; + + Device() : group({"default_profile", "profiles"}, + &Device::default_profile, + &Device::profiles), + default_profile(ipcgull::property_full_permissions, "") { + } + }; + + struct Config : public group { + std::optional, string_literal_of>> devices; + std::optional> ignore; + std::optional io_timeout; + std::optional workers; + + Config() : group({"devices", "ignore", "io_timeout", "workers"}, + &Config::devices, + &Config::ignore, + &Config::io_timeout, + &Config::workers) {} + }; +} + +#endif //LOGID_CONFIG_SCHEMA_H diff --git a/src/logid/config/types.h b/src/logid/config/types.h index ef17a75..2ff4e4f 100644 --- a/src/logid/config/types.h +++ b/src/logid/config/types.h @@ -1,462 +1,462 @@ -/* - * Copyright 2022 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_CONFIG_PRIMITIVE_H -#define LOGID_CONFIG_PRIMITIVE_H - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -// Containers are chosen specifically so that no iterator is invalidated. - -namespace logid::config { - void logError(const libconfig::Setting& setting, std::exception& e); - - template - struct config_io { - static_assert(std::is_base_of::value); - - static T get(const libconfig::Setting& parent, - const std::string& name) { - T t{}; - t._load(parent.lookup(name)); - return t; - } - - static T get(const libconfig::Setting& setting) { - T t{}; - t._load(setting); - return t; - } - - static void set(libconfig::Setting& parent, - const std::string& name, - const T& t) { - if (!parent.exists(name)) { - parent.add(name, libconfig::Setting::TypeGroup); - } else if (parent.lookup(name).getType() - != libconfig::Setting::TypeGroup) { - parent.remove(name); - parent.add(name, libconfig::Setting::TypeGroup); - } - t._save(parent.lookup(name)); - } - - static void set(libconfig::Setting& setting, const T& t) { - t._save(setting); - } - - static void append(libconfig::Setting& list, const T& t) { - auto& x = list.add(libconfig::Setting::TypeGroup); - set(x, t); - } - }; - - template - struct config_io> : public config_io { - }; - - template - struct primitive_io { - static T get(const libconfig::Setting& parent, - const std::string& name) { - return parent.lookup(name); - } - - static T get(const libconfig::Setting& setting) { - return setting; - } - - static void set(libconfig::Setting& parent, - const std::string& name, - const T& t) { - if (!parent.exists(name)) { - parent.add(name, TypeEnum); - } else if (parent.lookup(name).getType() != TypeEnum) { - parent.remove(name); - parent.add(name, TypeEnum); - } - set(parent.lookup(name), t); - } - - static void set(libconfig::Setting& setting, const T& t) { - setting = t; - } - - static void append(libconfig::Setting& list, const T& t) { - auto& x = list.add(TypeEnum); - set(x, t); - } - }; - - template - struct reinterpret_io { - static T get(const libconfig::Setting& parent, - const std::string& name) { - return static_cast(primitive_io::get(parent, name)); - } - - static T get(const libconfig::Setting& setting) { - return static_cast(primitive_io::get(setting)); - } - - static void set(libconfig::Setting& parent, - const std::string& name, - const T& t) { - primitive_io::set(parent, name, - static_cast(t)); - } - - static void set(libconfig::Setting& setting, const T& t) { - primitive_io::set(setting, - static_cast(t)); - } - - [[maybe_unused]] - static void append(libconfig::Setting& list, const T& t) { - primitive_io::append(list, - static_cast(t)); - } - }; - - template<> - struct config_io : public primitive_io { - }; - template<> - struct config_io : public reinterpret_io { - }; - template<> - struct config_io : public reinterpret_io { - }; - template<> - struct config_io : public reinterpret_io { - }; - template<> - struct config_io : public reinterpret_io { - }; - template<> - struct config_io : public primitive_io { - }; - template<> - struct config_io : public reinterpret_io { - }; - template<> - struct config_io : public reinterpret_io { - }; - template<> - struct config_io : public reinterpret_io { - }; - template<> - struct config_io : public primitive_io { - }; - template<> - struct config_io : - public reinterpret_io { - }; - template<> - struct config_io : public primitive_io { - }; - template<> - struct config_io : public primitive_io { - }; - template<> - struct config_io : public primitive_io { - }; - - template - struct config_io> { - private: - template - static std::variant try_each(const libconfig::Setting& setting) { - return config_io::get(setting); - } - - template - static std::variant try_each(const libconfig::Setting& setting) { - try { - return config_io::get(setting); - } catch (libconfig::SettingException& e) { - return try_each(setting); - } - } - - public: - static std::variant get(const libconfig::Setting& setting) { - return try_each(setting); - } - - static std::variant get(const libconfig::Setting& parent, - const std::string& name) { - return get(parent.lookup(name)); - } - - static void set(libconfig::Setting& setting, - const std::variant& t) { - std::visit([&setting](auto&& arg) { - config::set(setting, arg); - }, t); - } - - static void set(libconfig::Setting& parent, - const std::string& name, - const std::variant& t) { - std::visit([&parent, &name](auto&& arg) { - config::set(parent, name, arg); - }, t); - } - - [[maybe_unused]] - static void append(libconfig::Setting& list, const std::variant& t) { - std::visit([&list](auto&& arg) { - config::append(list, arg); - }, t); - } - }; - - template - struct config_io> { - static std::list get(const libconfig::Setting& setting) { - const auto size = setting.getLength(); - std::list t{}; - for (int i = 0; i < size; ++i) { - try { - t.emplace_back(config_io::get(setting[i])); - } catch (libconfig::SettingException& e) {} - } - return t; - } - - static std::list get(const libconfig::Setting& parent, const std::string& name) { - return get(parent.lookup(name)); - } - - static void set(libconfig::Setting& setting, const std::list& t) { - while (setting.getLength() != 0) - setting.remove((int) 0); - for (auto& x: t) { - config_io::append(setting, x); - } - } - - static void set(libconfig::Setting& parent, - const std::string& name, - const std::list& t) { - if (!parent.exists(name)) { - parent.add(name, libconfig::Setting::TypeList); - } else if (!parent.lookup(name).isList()) { - parent.remove(name); - parent.add(name, libconfig::Setting::TypeList); - } - set(parent.lookup(name), t); - } - - [[maybe_unused]] - static void append(libconfig::Setting& list, const std::list& t) { - auto& s = list.add(libconfig::Setting::TypeList); - set(s, t); - } - }; - - template - struct config_io> { - static std::set get(const libconfig::Setting& setting) { - const auto size = setting.getLength(); - std::set t; - for (int i = 0; i < size; ++i) { - try { - t.emplace(config_io::get(setting[i])); - } catch (libconfig::SettingException& e) {} - } - return t; - } - - static std::set get(const libconfig::Setting& parent, const std::string& name) { - return get(parent.lookup(name)); - } - - static void set(libconfig::Setting& setting, const std::set& t) { - while (setting.getLength() != 0) - setting.remove((int) 0); - for (auto& x: t) { - auto& s = setting.add(libconfig::Setting::TypeGroup); - config_io::set(s, x); - } - } - - static void set(libconfig::Setting& parent, - const std::string& name, - const std::set& t) { - if (!parent.exists(name)) { - parent.add(name, libconfig::Setting::TypeList); - } else if (!parent.lookup(name).isArray()) { - parent.remove(name); - parent.add(name, libconfig::Setting::TypeList); - } - set(parent.lookup(name), t); - } - - [[maybe_unused]] - static void append(libconfig::Setting& list, - const std::set& t) { - auto& s = list.add(libconfig::Setting::TypeList); - set(s, t); - } - }; - - template - struct config_io> { - static map get(const libconfig::Setting& setting) { - const auto size = setting.getLength(); - map t; - for (int i = 0; i < size; ++i) { - auto& s = setting[i]; - try { - t.emplace(config_io::get(s.lookup(KeyName::value)), - config_io::get(s)); - } catch (libconfig::SettingException& e) {} - } - return t; - } - - static map get( - const libconfig::Setting& parent, const std::string& name) { - return get(parent.lookup(name)); - } - - static void set(libconfig::Setting& setting, - const map& t) { - while (setting.getLength() != 0) - setting.remove((int) 0); - for (auto& x: t) { - auto& s = setting.add(libconfig::Setting::TypeGroup); - config_io::set(s, x.second); - config_io::set(s, KeyName::value, x.first); - } - } - - static void set(libconfig::Setting& parent, - const std::string& name, - const map& t) { - if (!parent.exists(name)) { - parent.add(name, libconfig::Setting::TypeList); - } else if (!parent.lookup(name).isArray()) { - parent.remove(name); - parent.add(name, libconfig::Setting::TypeList); - } - set(parent.lookup(name), t); - } - - [[maybe_unused]] - static void append(libconfig::Setting& list, const map& t) { - auto& s = list.add(libconfig::Setting::TypeList); - set(s, t); - } - }; - - template - struct config_io> { - static std::optional get(const libconfig::Setting& parent, - const std::string& name) { - if (parent.exists(name)) { - auto& setting = parent.lookup(name); - try { - return config_io::get(setting); - } catch (libconfig::SettingException& e) { - logError(setting, e); - return std::nullopt; - } - } else { - return std::nullopt; - } - } - - static void set(libconfig::Setting& parent, - const std::string& name, - const std::optional& t) { - if (t.has_value()) - config_io::set(parent, name, t.value()); - } - }; - - // Optionals may not appear as part of a list or array - template - struct config_io, Rest...>> { - static_assert(!sizeof(std::optional), "Invalid type"); - }; - - template - struct config_io>> { - static_assert(!sizeof(std::optional), "Invalid type"); - }; - - template - struct config_io>> { - static_assert(!sizeof(std::optional), "Invalid type"); - }; - - template - void set(libconfig::Setting& parent, - const std::string& name, - const T& t) { - config_io::set(parent, name, t); - } - - template - void set(libconfig::Setting& setting, const T& t) { - config_io::set(setting, t); - } - - - template - void append(libconfig::Setting& list, const T& t) { - config_io::append(list, t); - } - - template - auto get(const libconfig::Setting& setting) { - return config_io::get(setting); - } - - template - auto get(const libconfig::Setting& parent, const std::string& name) { - return config_io::get(parent, name); - } -} - -#endif //LOGID_CONFIG_PRIMITIVE_H +/* + * Copyright 2022 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_CONFIG_PRIMITIVE_H +#define LOGID_CONFIG_PRIMITIVE_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// Containers are chosen specifically so that no iterator is invalidated. + +namespace logid::config { + void logError(const libconfig::Setting& setting, std::exception& e); + + template + struct config_io { + static_assert(std::is_base_of::value); + + static T get(const libconfig::Setting& parent, + const std::string& name) { + T t{}; + t._load(parent.lookup(name)); + return t; + } + + static T get(const libconfig::Setting& setting) { + T t{}; + t._load(setting); + return t; + } + + static void set(libconfig::Setting& parent, + const std::string& name, + const T& t) { + if (!parent.exists(name)) { + parent.add(name, libconfig::Setting::TypeGroup); + } else if (parent.lookup(name).getType() + != libconfig::Setting::TypeGroup) { + parent.remove(name); + parent.add(name, libconfig::Setting::TypeGroup); + } + t._save(parent.lookup(name)); + } + + static void set(libconfig::Setting& setting, const T& t) { + t._save(setting); + } + + static void append(libconfig::Setting& list, const T& t) { + auto& x = list.add(libconfig::Setting::TypeGroup); + set(x, t); + } + }; + + template + struct config_io> : public config_io { + }; + + template + struct primitive_io { + static T get(const libconfig::Setting& parent, + const std::string& name) { + return parent.lookup(name); + } + + static T get(const libconfig::Setting& setting) { + return setting; + } + + static void set(libconfig::Setting& parent, + const std::string& name, + const T& t) { + if (!parent.exists(name)) { + parent.add(name, TypeEnum); + } else if (parent.lookup(name).getType() != TypeEnum) { + parent.remove(name); + parent.add(name, TypeEnum); + } + set(parent.lookup(name), t); + } + + static void set(libconfig::Setting& setting, const T& t) { + setting = t; + } + + static void append(libconfig::Setting& list, const T& t) { + auto& x = list.add(TypeEnum); + set(x, t); + } + }; + + template + struct reinterpret_io { + static T get(const libconfig::Setting& parent, + const std::string& name) { + return static_cast(primitive_io::get(parent, name)); + } + + static T get(const libconfig::Setting& setting) { + return static_cast(primitive_io::get(setting)); + } + + static void set(libconfig::Setting& parent, + const std::string& name, + const T& t) { + primitive_io::set(parent, name, + static_cast(t)); + } + + static void set(libconfig::Setting& setting, const T& t) { + primitive_io::set(setting, + static_cast(t)); + } + + [[maybe_unused]] + static void append(libconfig::Setting& list, const T& t) { + primitive_io::append(list, + static_cast(t)); + } + }; + + template<> + struct config_io : public primitive_io { + }; + template<> + struct config_io : public reinterpret_io { + }; + template<> + struct config_io : public reinterpret_io { + }; + template<> + struct config_io : public reinterpret_io { + }; + template<> + struct config_io : public reinterpret_io { + }; + template<> + struct config_io : public primitive_io { + }; + template<> + struct config_io : public reinterpret_io { + }; + template<> + struct config_io : public reinterpret_io { + }; + template<> + struct config_io : public reinterpret_io { + }; + template<> + struct config_io : public primitive_io { + }; + template<> + struct config_io : + public reinterpret_io { + }; + template<> + struct config_io : public primitive_io { + }; + template<> + struct config_io : public primitive_io { + }; + template<> + struct config_io : public primitive_io { + }; + + template + struct config_io> { + private: + template + static std::variant try_each(const libconfig::Setting& setting) { + return config_io::get(setting); + } + + template + static std::variant try_each(const libconfig::Setting& setting) { + try { + return config_io::get(setting); + } catch (libconfig::SettingException& e) { + return try_each(setting); + } + } + + public: + static std::variant get(const libconfig::Setting& setting) { + return try_each(setting); + } + + static std::variant get(const libconfig::Setting& parent, + const std::string& name) { + return get(parent.lookup(name)); + } + + static void set(libconfig::Setting& setting, + const std::variant& t) { + std::visit([&setting](auto&& arg) { + config::set(setting, arg); + }, t); + } + + static void set(libconfig::Setting& parent, + const std::string& name, + const std::variant& t) { + std::visit([&parent, &name](auto&& arg) { + config::set(parent, name, arg); + }, t); + } + + [[maybe_unused]] + static void append(libconfig::Setting& list, const std::variant& t) { + std::visit([&list](auto&& arg) { + config::append(list, arg); + }, t); + } + }; + + template + struct config_io> { + static std::list get(const libconfig::Setting& setting) { + const auto size = setting.getLength(); + std::list t{}; + for (int i = 0; i < size; ++i) { + try { + t.emplace_back(config_io::get(setting[i])); + } catch (libconfig::SettingException& e) {} + } + return t; + } + + static std::list get(const libconfig::Setting& parent, const std::string& name) { + return get(parent.lookup(name)); + } + + static void set(libconfig::Setting& setting, const std::list& t) { + while (setting.getLength() != 0) + setting.remove((int) 0); + for (auto& x: t) { + config_io::append(setting, x); + } + } + + static void set(libconfig::Setting& parent, + const std::string& name, + const std::list& t) { + if (!parent.exists(name)) { + parent.add(name, libconfig::Setting::TypeList); + } else if (!parent.lookup(name).isList()) { + parent.remove(name); + parent.add(name, libconfig::Setting::TypeList); + } + set(parent.lookup(name), t); + } + + [[maybe_unused]] + static void append(libconfig::Setting& list, const std::list& t) { + auto& s = list.add(libconfig::Setting::TypeList); + set(s, t); + } + }; + + template + struct config_io> { + static std::set get(const libconfig::Setting& setting) { + const auto size = setting.getLength(); + std::set t; + for (int i = 0; i < size; ++i) { + try { + t.emplace(config_io::get(setting[i])); + } catch (libconfig::SettingException& e) {} + } + return t; + } + + static std::set get(const libconfig::Setting& parent, const std::string& name) { + return get(parent.lookup(name)); + } + + static void set(libconfig::Setting& setting, const std::set& t) { + while (setting.getLength() != 0) + setting.remove((int) 0); + for (auto& x: t) { + auto& s = setting.add(libconfig::Setting::TypeGroup); + config_io::set(s, x); + } + } + + static void set(libconfig::Setting& parent, + const std::string& name, + const std::set& t) { + if (!parent.exists(name)) { + parent.add(name, libconfig::Setting::TypeList); + } else if (!parent.lookup(name).isArray()) { + parent.remove(name); + parent.add(name, libconfig::Setting::TypeList); + } + set(parent.lookup(name), t); + } + + [[maybe_unused]] + static void append(libconfig::Setting& list, + const std::set& t) { + auto& s = list.add(libconfig::Setting::TypeList); + set(s, t); + } + }; + + template + struct config_io> { + static map get(const libconfig::Setting& setting) { + const auto size = setting.getLength(); + map t; + for (int i = 0; i < size; ++i) { + auto& s = setting[i]; + try { + t.emplace(config_io::get(s.lookup(KeyName::value)), + config_io::get(s)); + } catch (libconfig::SettingException& e) {} + } + return t; + } + + static map get( + const libconfig::Setting& parent, const std::string& name) { + return get(parent.lookup(name)); + } + + static void set(libconfig::Setting& setting, + const map& t) { + while (setting.getLength() != 0) + setting.remove((int) 0); + for (auto& x: t) { + auto& s = setting.add(libconfig::Setting::TypeGroup); + config_io::set(s, x.second); + config_io::set(s, KeyName::value, x.first); + } + } + + static void set(libconfig::Setting& parent, + const std::string& name, + const map& t) { + if (!parent.exists(name)) { + parent.add(name, libconfig::Setting::TypeList); + } else if (!parent.lookup(name).isArray()) { + parent.remove(name); + parent.add(name, libconfig::Setting::TypeList); + } + set(parent.lookup(name), t); + } + + [[maybe_unused]] + static void append(libconfig::Setting& list, const map& t) { + auto& s = list.add(libconfig::Setting::TypeList); + set(s, t); + } + }; + + template + struct config_io> { + static std::optional get(const libconfig::Setting& parent, + const std::string& name) { + if (parent.exists(name)) { + auto& setting = parent.lookup(name); + try { + return config_io::get(setting); + } catch (libconfig::SettingException& e) { + logError(setting, e); + return std::nullopt; + } + } else { + return std::nullopt; + } + } + + static void set(libconfig::Setting& parent, + const std::string& name, + const std::optional& t) { + if (t.has_value()) + config_io::set(parent, name, t.value()); + } + }; + + // Optionals may not appear as part of a list or array + template + struct config_io, Rest...>> { + static_assert(!sizeof(std::optional), "Invalid type"); + }; + + template + struct config_io>> { + static_assert(!sizeof(std::optional), "Invalid type"); + }; + + template + struct config_io>> { + static_assert(!sizeof(std::optional), "Invalid type"); + }; + + template + void set(libconfig::Setting& parent, + const std::string& name, + const T& t) { + config_io::set(parent, name, t); + } + + template + void set(libconfig::Setting& setting, const T& t) { + config_io::set(setting, t); + } + + + template + void append(libconfig::Setting& list, const T& t) { + config_io::append(list, t); + } + + template + auto get(const libconfig::Setting& setting) { + return config_io::get(setting); + } + + template + auto get(const libconfig::Setting& parent, const std::string& name) { + return config_io::get(parent, name); + } +} + +#endif //LOGID_CONFIG_PRIMITIVE_H diff --git a/src/logid/features/DPI.cpp b/src/logid/features/DPI.cpp index fdbac5c..187c461 100644 --- a/src/logid/features/DPI.cpp +++ b/src/logid/features/DPI.cpp @@ -1,209 +1,209 @@ -/* - * Copyright 2019-2023 PixlOne - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * 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 - -using namespace logid::features; -using namespace logid::backend; - -uint16_t getClosestDPI(const 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 (uint16_t) (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->activeProfile().dpi) { - try { - _adjustable_dpi = std::make_shared - (&device->hidpp20()); - } catch (hidpp20::UnsupportedFeature& e) { - throw UnsupportedFeature(); - } - - _ipc_interface = _device->ipcNode()->make_interface(this); -} - -void DPI::configure() { - std::shared_lock lock(_config_mutex); - - if (_config.get().has_value()) { - const auto& config = _config.get().value(); - if (std::holds_alternative(config)) { - const auto& dpi = std::get(config); - _fillDPILists(0); - std::shared_lock dpi_lock(_dpi_list_mutex); - if (dpi != 0) { - _adjustable_dpi->setSensorDPI(0, getClosestDPI(_dpi_lists.at(0), dpi)); - } - } else { - const auto& dpis = std::get>(config); - int i = 0; - _fillDPILists(dpis.size() - 1); - std::shared_lock dpi_lock(_dpi_list_mutex); - for (const auto& dpi: dpis) { - if (dpi != 0) { - _adjustable_dpi->setSensorDPI(i, getClosestDPI(_dpi_lists.at(i), dpi)); - ++i; - } - } - } - } -} - -void DPI::listen() { -} - -void DPI::setProfile(config::Profile& profile) { - std::unique_lock lock(_config_mutex); - _config = profile.dpi; -} - -uint16_t DPI::getDPI(uint8_t sensor) { - return _adjustable_dpi->getSensorDPI(sensor); -} - -void DPI::setDPI(uint16_t dpi, uint8_t sensor) { - if (dpi == 0) - return; - _fillDPILists(sensor); - std::shared_lock lock(_dpi_list_mutex); - auto dpi_list = _dpi_lists.at(sensor); - _adjustable_dpi->setSensorDPI(sensor, getClosestDPI(dpi_list, dpi)); -} - -void DPI::_fillDPILists(uint8_t sensor) { - bool needs_fill; - { - std::shared_lock lock(_dpi_list_mutex); - needs_fill = _dpi_lists.size() <= sensor; - } - if (needs_fill) { - std::unique_lock lock(_dpi_list_mutex); - for (std::size_t i = _dpi_lists.size(); i <= sensor; i++) { - _dpi_lists.push_back(_adjustable_dpi->getSensorDPIList(i)); - } - } -} - -DPI::IPC::IPC(DPI* parent) : ipcgull::interface( - SERVICE_ROOT_NAME ".DPI", { - {"GetSensors", {this, &IPC::getSensors, {"sensors"}}}, - {"GetDPIs", {this, &IPC::getDPIs, {"sensor"}, {"dpis", "dpiStep", "range"}}}, - {"GetDPI", {this, &IPC::getDPI, {"sensor"}, {"dpi"}}}, - {"SetDPI", {this, &IPC::setDPI, {"dpi", "sensor"}}} - }, {}, {}), _parent(*parent) { -} - -uint8_t DPI::IPC::getSensors() const { - return _parent._dpi_lists.size(); -} - -std::tuple, uint16_t, bool> DPI::IPC::getDPIs(uint8_t sensor) const { - _parent._fillDPILists(sensor); - std::shared_lock lock(_parent._dpi_list_mutex); - auto& dpi_list = _parent._dpi_lists.at(sensor); - return {dpi_list.dpis, dpi_list.dpiStep, dpi_list.isRange}; -} - -uint16_t DPI::IPC::getDPI(uint8_t sensor) const { - std::shared_lock lock(_parent._config_mutex); - auto& config = _parent._config.get(); - - if (!config.has_value()) - return _parent.getDPI(sensor); - - if (std::holds_alternative(config.value())) { - if (sensor == 0) - return std::get(config.value()); - else - return _parent.getDPI(sensor); - } - - const auto& list = std::get>(config.value()); - - if (list.size() > sensor) { - auto it = list.begin(); - std::advance(it, sensor); - return *it; - } else { - return _parent.getDPI(sensor); - } -} - -void DPI::IPC::setDPI(uint16_t dpi, uint8_t sensor) { - std::unique_lock lock(_parent._config_mutex); - auto& config = _parent._config.get(); - - if (!config.has_value()) - config.emplace(std::list()); - - if (std::holds_alternative(config.value())) { - if (sensor == 0) { - config.value() = dpi; - } else { - auto list = std::list(sensor + 1, 0); - *list.rbegin() = dpi; - *list.begin() = dpi; - config.value() = list; - } - } else { - auto& list = std::get>(config.value()); - - while (list.size() <= sensor) { - list.emplace_back(0); - } - - if (list.size() == (size_t) (sensor + 1)) { - *list.rbegin() = dpi; - } else { - auto it = list.begin(); - std::advance(it, sensor); - *it = dpi; - } - } - - _parent.setDPI(dpi, sensor); -} +/* + * Copyright 2019-2023 PixlOne + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * 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 + +using namespace logid::features; +using namespace logid::backend; + +uint16_t getClosestDPI(const 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 (uint16_t) (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->activeProfile().dpi) { + try { + _adjustable_dpi = std::make_shared + (&device->hidpp20()); + } catch (hidpp20::UnsupportedFeature& e) { + throw UnsupportedFeature(); + } + + _ipc_interface = _device->ipcNode()->make_interface(this); +} + +void DPI::configure() { + std::shared_lock lock(_config_mutex); + + if (_config.get().has_value()) { + const auto& config = _config.get().value(); + if (std::holds_alternative(config)) { + const auto& dpi = std::get(config); + _fillDPILists(0); + std::shared_lock dpi_lock(_dpi_list_mutex); + if (dpi != 0) { + _adjustable_dpi->setSensorDPI(0, getClosestDPI(_dpi_lists.at(0), dpi)); + } + } else { + const auto& dpis = std::get>(config); + int i = 0; + _fillDPILists(dpis.size() - 1); + std::shared_lock dpi_lock(_dpi_list_mutex); + for (const auto& dpi: dpis) { + if (dpi != 0) { + _adjustable_dpi->setSensorDPI(i, getClosestDPI(_dpi_lists.at(i), dpi)); + ++i; + } + } + } + } +} + +void DPI::listen() { +} + +void DPI::setProfile(config::Profile& profile) { + std::unique_lock lock(_config_mutex); + _config = profile.dpi; +} + +uint16_t DPI::getDPI(uint8_t sensor) { + return _adjustable_dpi->getSensorDPI(sensor); +} + +void DPI::setDPI(uint16_t dpi, uint8_t sensor) { + if (dpi == 0) + return; + _fillDPILists(sensor); + std::shared_lock lock(_dpi_list_mutex); + auto dpi_list = _dpi_lists.at(sensor); + _adjustable_dpi->setSensorDPI(sensor, getClosestDPI(dpi_list, dpi)); +} + +void DPI::_fillDPILists(uint8_t sensor) { + bool needs_fill; + { + std::shared_lock lock(_dpi_list_mutex); + needs_fill = _dpi_lists.size() <= sensor; + } + if (needs_fill) { + std::unique_lock lock(_dpi_list_mutex); + for (std::size_t i = _dpi_lists.size(); i <= sensor; i++) { + _dpi_lists.push_back(_adjustable_dpi->getSensorDPIList(i)); + } + } +} + +DPI::IPC::IPC(DPI* parent) : ipcgull::interface( + SERVICE_ROOT_NAME ".DPI", { + {"GetSensors", {this, &IPC::getSensors, {"sensors"}}}, + {"GetDPIs", {this, &IPC::getDPIs, {"sensor"}, {"dpis", "dpiStep", "range"}}}, + {"GetDPI", {this, &IPC::getDPI, {"sensor"}, {"dpi"}}}, + {"SetDPI", {this, &IPC::setDPI, {"dpi", "sensor"}}} + }, {}, {}), _parent(*parent) { +} + +uint8_t DPI::IPC::getSensors() const { + return _parent._dpi_lists.size(); +} + +std::tuple, uint16_t, bool> DPI::IPC::getDPIs(uint8_t sensor) const { + _parent._fillDPILists(sensor); + std::shared_lock lock(_parent._dpi_list_mutex); + auto& dpi_list = _parent._dpi_lists.at(sensor); + return {dpi_list.dpis, dpi_list.dpiStep, dpi_list.isRange}; +} + +uint16_t DPI::IPC::getDPI(uint8_t sensor) const { + std::shared_lock lock(_parent._config_mutex); + auto& config = _parent._config.get(); + + if (!config.has_value()) + return _parent.getDPI(sensor); + + if (std::holds_alternative(config.value())) { + if (sensor == 0) + return std::get(config.value()); + else + return _parent.getDPI(sensor); + } + + const auto& list = std::get>(config.value()); + + if (list.size() > sensor) { + auto it = list.begin(); + std::advance(it, sensor); + return *it; + } else { + return _parent.getDPI(sensor); + } +} + +void DPI::IPC::setDPI(uint16_t dpi, uint8_t sensor) { + std::unique_lock lock(_parent._config_mutex); + auto& config = _parent._config.get(); + + if (!config.has_value()) + config.emplace(std::list()); + + if (std::holds_alternative(config.value())) { + if (sensor == 0) { + config.value() = dpi; + } else { + auto list = std::list(sensor + 1, 0); + *list.rbegin() = dpi; + *list.begin() = dpi; + config.value() = list; + } + } else { + auto& list = std::get>(config.value()); + + while (list.size() <= sensor) { + list.emplace_back(0); + } + + if (list.size() == (size_t) (sensor + 1)) { + *list.rbegin() = dpi; + } else { + auto it = list.begin(); + std::advance(it, sensor); + *it = dpi; + } + } + + _parent.setDPI(dpi, sensor); +} diff --git a/src/logid/features/DPI.h b/src/logid/features/DPI.h index 6fff8d8..5c5786c 100644 --- a/src/logid/features/DPI.h +++ b/src/logid/features/DPI.h @@ -1,73 +1,73 @@ -/* - * Copyright 2019-2023 PixlOne - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * 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_DPI_H -#define LOGID_FEATURE_DPI_H - -#include -#include -#include -#include -#include - -namespace logid::features { - class DPI : public DeviceFeature { - public: - void configure() final; - - void listen() final; - - void setProfile(config::Profile& profile) final; - - uint16_t getDPI(uint8_t sensor = 0); - - void setDPI(uint16_t dpi, uint8_t sensor = 0); - - protected: - explicit DPI(Device* dev); - - private: - void _fillDPILists(uint8_t sensor); - - class IPC : public ipcgull::interface { - public: - explicit IPC(DPI* parent); - - [[nodiscard]] uint8_t getSensors() const; - - [[nodiscard]] std::tuple, uint16_t, bool> getDPIs( - uint8_t sensor) const; - - [[nodiscard]] uint16_t getDPI(uint8_t sensor) const; - - void setDPI(uint16_t dpi, uint8_t sensor); - - private: - DPI& _parent; - }; - - mutable std::shared_mutex _config_mutex; - std::reference_wrapper> _config; - std::shared_ptr _adjustable_dpi; - mutable std::shared_mutex _dpi_list_mutex; - std::vector _dpi_lists; - - std::shared_ptr _ipc_interface; - }; -} - -#endif //LOGID_FEATURE_DPI_H +/* + * Copyright 2019-2023 PixlOne + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * 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_DPI_H +#define LOGID_FEATURE_DPI_H + +#include +#include +#include +#include +#include + +namespace logid::features { + class DPI : public DeviceFeature { + public: + void configure() final; + + void listen() final; + + void setProfile(config::Profile& profile) final; + + uint16_t getDPI(uint8_t sensor = 0); + + void setDPI(uint16_t dpi, uint8_t sensor = 0); + + protected: + explicit DPI(Device* dev); + + private: + void _fillDPILists(uint8_t sensor); + + class IPC : public ipcgull::interface { + public: + explicit IPC(DPI* parent); + + [[nodiscard]] uint8_t getSensors() const; + + [[nodiscard]] std::tuple, uint16_t, bool> getDPIs( + uint8_t sensor) const; + + [[nodiscard]] uint16_t getDPI(uint8_t sensor) const; + + void setDPI(uint16_t dpi, uint8_t sensor); + + private: + DPI& _parent; + }; + + mutable std::shared_mutex _config_mutex; + std::reference_wrapper> _config; + std::shared_ptr _adjustable_dpi; + mutable std::shared_mutex _dpi_list_mutex; + std::vector _dpi_lists; + + std::shared_ptr _ipc_interface; + }; +} + +#endif //LOGID_FEATURE_DPI_H diff --git a/src/logid/features/DeviceFeature.h b/src/logid/features/DeviceFeature.h index f41bdf9..8c21f64 100644 --- a/src/logid/features/DeviceFeature.h +++ b/src/logid/features/DeviceFeature.h @@ -1,93 +1,93 @@ -/* - * Copyright 2019-2023 PixlOne - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * 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 - -#include -#include - -namespace logid { - class Device; -} - -namespace logid::config { - struct Profile; -} - -namespace logid::features { - class UnsupportedFeature : public std::exception { - public: - UnsupportedFeature() = default; - - [[nodiscard]] const char* what() const noexcept override { - return "Unsupported feature"; - } - }; - - template - class _featureWrapper : public T { - friend class DeviceFeature; - - public: - template - explicit _featureWrapper(Args... args) : T(std::forward(args)...) {} - - template - static std::shared_ptr make(Args... args) { - return std::make_shared<_featureWrapper>(std::forward(args)...); - } - }; - - class DeviceFeature { - std::weak_ptr _self; - public: - virtual void configure() = 0; - - virtual void listen() = 0; - - virtual void setProfile(config::Profile& profile) = 0; - - virtual ~DeviceFeature() = default; - - DeviceFeature(const DeviceFeature&) = delete; - - DeviceFeature(DeviceFeature&&) = delete; - - protected: - explicit DeviceFeature(Device* dev) : _device(dev) {} - - Device* _device; - - template - [[nodiscard]] std::weak_ptr self() const { - return std::dynamic_pointer_cast(_self.lock()); - } - - public: - template - static std::shared_ptr make(Args... args) { - auto feature = _featureWrapper::make(std::forward(args)...); - feature->_self = feature; - - return feature; - } - }; -} - -#endif //LOGID_FEATURES_DEVICEFEATURE_H +/* + * Copyright 2019-2023 PixlOne + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * 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 + +#include +#include + +namespace logid { + class Device; +} + +namespace logid::config { + struct Profile; +} + +namespace logid::features { + class UnsupportedFeature : public std::exception { + public: + UnsupportedFeature() = default; + + [[nodiscard]] const char* what() const noexcept override { + return "Unsupported feature"; + } + }; + + template + class _featureWrapper : public T { + friend class DeviceFeature; + + public: + template + explicit _featureWrapper(Args... args) : T(std::forward(args)...) {} + + template + static std::shared_ptr make(Args... args) { + return std::make_shared<_featureWrapper>(std::forward(args)...); + } + }; + + class DeviceFeature { + std::weak_ptr _self; + public: + virtual void configure() = 0; + + virtual void listen() = 0; + + virtual void setProfile(config::Profile& profile) = 0; + + virtual ~DeviceFeature() = default; + + DeviceFeature(const DeviceFeature&) = delete; + + DeviceFeature(DeviceFeature&&) = delete; + + protected: + explicit DeviceFeature(Device* dev) : _device(dev) {} + + Device* _device; + + template + [[nodiscard]] std::weak_ptr self() const { + return std::dynamic_pointer_cast(_self.lock()); + } + + public: + template + static std::shared_ptr make(Args... args) { + auto feature = _featureWrapper::make(std::forward(args)...); + feature->_self = feature; + + return feature; + } + }; +} + +#endif //LOGID_FEATURES_DEVICEFEATURE_H diff --git a/src/logid/features/DeviceStatus.cpp b/src/logid/features/DeviceStatus.cpp index c9a0544..1c7b0c6 100644 --- a/src/logid/features/DeviceStatus.cpp +++ b/src/logid/features/DeviceStatus.cpp @@ -1,67 +1,67 @@ -/* - * Copyright 2019-2023 PixlOne - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * 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 - -using namespace logid::features; -using namespace logid::backend; - -DeviceStatus::DeviceStatus(logid::Device* dev) : DeviceFeature(dev) { - /* This feature is redundant on receivers since the receiver - * handles wakeup/sleep events. If the device is connected on a - * receiver, pretend this feature is unsupported. - */ - if (dev->hidpp20().deviceIndex() >= hidpp::WirelessDevice1 && - dev->hidpp20().deviceIndex() <= hidpp::WirelessDevice6) - throw UnsupportedFeature(); - - try { - _wireless_device_status = std::make_shared(&dev->hidpp20()); - } catch (hidpp20::UnsupportedFeature& e) { - throw UnsupportedFeature(); - } -} - -void DeviceStatus::configure() { - // Do nothing -} - -void DeviceStatus::listen() { - if (_ev_handler.empty()) { - _ev_handler = _device->hidpp20().addEventHandler( - {[index = _wireless_device_status->featureIndex()]( - const hidpp::Report& report) -> bool { - return report.feature() == index && - report.function() == - hidpp20::WirelessDeviceStatus::StatusBroadcast; - }, - [self_weak = self()]( - const hidpp::Report& report) { - auto event = hidpp20::WirelessDeviceStatus::statusBroadcastEvent(report); - if (event.reconfNeeded) - run_task_after([self_weak]() { - if (auto self = self_weak.lock()) - self->_device->wakeup(); - }, std::chrono::milliseconds(100)); - } - }); - } -} - -void DeviceStatus::setProfile(config::Profile&) { -} +/* + * Copyright 2019-2023 PixlOne + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * 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 + +using namespace logid::features; +using namespace logid::backend; + +DeviceStatus::DeviceStatus(logid::Device* dev) : DeviceFeature(dev) { + /* This feature is redundant on receivers since the receiver + * handles wakeup/sleep events. If the device is connected on a + * receiver, pretend this feature is unsupported. + */ + if (dev->hidpp20().deviceIndex() >= hidpp::WirelessDevice1 && + dev->hidpp20().deviceIndex() <= hidpp::WirelessDevice6) + throw UnsupportedFeature(); + + try { + _wireless_device_status = std::make_shared(&dev->hidpp20()); + } catch (hidpp20::UnsupportedFeature& e) { + throw UnsupportedFeature(); + } +} + +void DeviceStatus::configure() { + // Do nothing +} + +void DeviceStatus::listen() { + if (_ev_handler.empty()) { + _ev_handler = _device->hidpp20().addEventHandler( + {[index = _wireless_device_status->featureIndex()]( + const hidpp::Report& report) -> bool { + return report.feature() == index && + report.function() == + hidpp20::WirelessDeviceStatus::StatusBroadcast; + }, + [self_weak = self()]( + const hidpp::Report& report) { + auto event = hidpp20::WirelessDeviceStatus::statusBroadcastEvent(report); + if (event.reconfNeeded) + run_task_after([self_weak]() { + if (auto self = self_weak.lock()) + self->_device->wakeup(); + }, std::chrono::milliseconds(100)); + } + }); + } +} + +void DeviceStatus::setProfile(config::Profile&) { +} diff --git a/src/logid/features/DeviceStatus.h b/src/logid/features/DeviceStatus.h index acd98fa..86e8af1 100644 --- a/src/logid/features/DeviceStatus.h +++ b/src/logid/features/DeviceStatus.h @@ -1,44 +1,44 @@ -/* - * Copyright 2019-2023 PixlOne - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * 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_DEVICESTATUS_H -#define LOGID_FEATURE_DEVICESTATUS_H - - -#include -#include -#include - -namespace logid::features { - class DeviceStatus : public DeviceFeature { - public: - void configure() final; - - void listen() final; - - void setProfile(config::Profile& profile) final; - - protected: - explicit DeviceStatus(Device* dev); - - private: - EventHandlerLock _ev_handler; - std::shared_ptr _wireless_device_status; - }; -} - -#endif //LOGID_FEATURE_DEVICESTATUS_H +/* + * Copyright 2019-2023 PixlOne + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * 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_DEVICESTATUS_H +#define LOGID_FEATURE_DEVICESTATUS_H + + +#include +#include +#include + +namespace logid::features { + class DeviceStatus : public DeviceFeature { + public: + void configure() final; + + void listen() final; + + void setProfile(config::Profile& profile) final; + + protected: + explicit DeviceStatus(Device* dev); + + private: + EventHandlerLock _ev_handler; + std::shared_ptr _wireless_device_status; + }; +} + +#endif //LOGID_FEATURE_DEVICESTATUS_H diff --git a/src/logid/features/HiresScroll.cpp b/src/logid/features/HiresScroll.cpp index c1c4587..e34c05b 100644 --- a/src/logid/features/HiresScroll.cpp +++ b/src/logid/features/HiresScroll.cpp @@ -1,318 +1,318 @@ -/* - * Copyright 2019-2023 PixlOne - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * 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 - -using namespace logid; -using namespace logid::features; -using namespace logid::backend; - -HiresScroll::HiresScroll(Device* dev) : - DeviceFeature(dev), - _config(dev->activeProfile().hiresscroll), _mode(0), - _mask(0), - _node(dev->ipcNode()->make_child("hires_scroll")), - _up_node(_node->make_child("up")), - _down_node(_node->make_child("down")) { - - try { - _hires_scroll = std::make_shared(&dev->hidpp20()); - } catch (hidpp20::UnsupportedFeature& e) { - throw UnsupportedFeature(); - } - - _makeConfig(); - - _last_scroll = std::chrono::system_clock::now(); - - _ipc_interface = dev->ipcNode()->make_interface(this); -} - -void HiresScroll::_makeConfig() { - auto& config = _config.get(); - _mode = 0; - _mask = 0; - - if (config.has_value()) { - if (std::holds_alternative(config.value())) { - config::HiresScroll conf{}; - conf.hires = std::get(config.value()); - conf.invert = false; - conf.target = false; - _mask |= hidpp20::HiresScroll::Mode::HiRes; - config.value() = conf; - } - auto& conf = std::get(config.value()); - if (conf.hires.has_value()) { - _mask |= hidpp20::HiresScroll::Mode::HiRes; - if (conf.hires.value()) - _mode |= hidpp20::HiresScroll::Mode::HiRes; - } - if (conf.invert.has_value()) { - _mask |= hidpp20::HiresScroll::Mode::Inverted; - if (conf.invert.value()) - _mode |= hidpp20::HiresScroll::Mode::Inverted; - } - if (conf.target.has_value()) { - _mask |= hidpp20::HiresScroll::Mode::Target; - if (conf.target.value()) - _mode |= hidpp20::HiresScroll::Mode::Target; - } - - _makeGesture(_up_gesture, conf.up, "up"); - _makeGesture(_down_gesture, conf.down, "down"); - } -} - -void HiresScroll::configure() { - std::shared_lock lock(_config_mutex); - _configure(); -} - -void HiresScroll::_configure() { - auto mode = _hires_scroll->getMode(); - mode &= ~_mask; - mode |= (_mode & _mask); - _hires_scroll->setMode(mode); -} - -void HiresScroll::listen() { - std::shared_lock lock(_config_mutex); - if (_ev_handler.empty()) { - _ev_handler = _device->hidpp20().addEventHandler( - {[index = _hires_scroll->featureIndex()]( - const hidpp::Report& report) -> bool { - return (report.feature() == index) && - (report.function() == hidpp20::HiresScroll::WheelMovement); - }, - [self_weak = self()](const hidpp::Report& report) { - if (auto self = self_weak.lock()) - self->_handleScroll(self->_hires_scroll->wheelMovementEvent(report)); - } - }); - } -} - -void HiresScroll::setProfile(config::Profile& profile) { - std::unique_lock lock(_config_mutex); - - _up_gesture.reset(); - _down_gesture.reset(); - _config = profile.hiresscroll; - _makeConfig(); -} - -uint8_t HiresScroll::getMode() { - return _hires_scroll->getMode(); -} - -void HiresScroll::setMode(uint8_t mode) { - _hires_scroll->setMode(mode); -} - -void HiresScroll::_makeGesture(std::shared_ptr& gesture, - std::optional& config, - const std::string& direction) { - if (config.has_value()) { - gesture = actions::Gesture::makeGesture(_device, config.value(), - _node->make_child(direction)); - - _fixGesture(gesture); - } else { - gesture.reset(); - } -} - -void HiresScroll::_fixGesture(const std::shared_ptr& gesture) { - try { - auto axis = std::dynamic_pointer_cast(gesture); - if (axis) - axis->setHiresMultiplier(_hires_scroll->getCapabilities().multiplier); - } catch (std::bad_cast& e) {} - if (gesture) - gesture->press(true); -} - -void HiresScroll::_handleScroll(hidpp20::HiresScroll::WheelStatus event) { - std::shared_lock lock(_config_mutex); - auto now = std::chrono::system_clock::now(); - if (std::chrono::duration_cast(now - _last_scroll).count() >= 1) { - if (_up_gesture) { - _up_gesture->release(false); - _up_gesture->press(true); - } - if (_down_gesture) { - _down_gesture->release(false); - _down_gesture->press(true); - } - - _last_direction = 0; - } - - if (event.deltaV > 0) { - if (_last_direction == -1) { - if (_down_gesture) { - _down_gesture->release(false); - _down_gesture->press(true); - } - } - if (_up_gesture) - _up_gesture->move(event.deltaV); - _last_direction = 1; - } else if (event.deltaV < 0) { - if (_last_direction == 1) { - if (_up_gesture) { - _up_gesture->release(false); - _up_gesture->press(true); - } - } - if (_down_gesture) - _down_gesture->move((int16_t) -event.deltaV); - _last_direction = -1; - } - - _last_scroll = now; -} - -HiresScroll::IPC::IPC(HiresScroll* parent) : ipcgull::interface( - SERVICE_ROOT_NAME ".HiresScroll", { - {"GetConfig", {this, &IPC::getConfig, {"hires", "invert", "target"}}}, - {"SetHires", {this, &IPC::setHires, {"hires"}}}, - {"SetInvert", {this, &IPC::setInvert, {"invert"}}}, - {"SetTarget", {this, &IPC::setTarget, {"target"}}}, - {"SetUp", {this, &IPC::setUp, {"type"}}}, - {"SetDown", {this, &IPC::setDown, {"type"}}}, - }, {}, {}), _parent(*parent) { -} - -std::tuple HiresScroll::IPC::getConfig() const { - std::shared_lock lock(_parent._config_mutex); - - auto& config = _parent._config.get(); - - if (config.has_value()) { - if (std::holds_alternative(config.value())) { - return {std::get(config.value()), false, false}; - } else { - const auto& config_obj = std::get(config.value()); - return { - config_obj.hires.value_or(true), - config_obj.invert.value_or(false), - config_obj.target.value_or(false) - }; - } - } else { - return {true, false, false}; - } -} - -config::HiresScroll& HiresScroll::IPC::_parentConfig() { - auto& config = _parent._config.get(); - if (!config.has_value()) { - config = config::HiresScroll(); - } else if (std::holds_alternative(config.value())) { - bool hires = std::get(config.value()); - auto new_config = config::HiresScroll(); - new_config.hires = hires; - config = new_config; - } - - return std::get(config.value()); -} - -void HiresScroll::IPC::setHires(bool hires) { - std::unique_lock lock(_parent._config_mutex); - _parentConfig().hires = hires; - - _parent._mask |= hidpp20::HiresScroll::Mode::HiRes; - if (hires) - _parent._mode |= hidpp20::HiresScroll::Mode::HiRes; - else - _parent._mode &= ~hidpp20::HiresScroll::Mode::HiRes; - - _parent._configure(); -} - -void HiresScroll::IPC::setInvert(bool invert) { - std::unique_lock lock(_parent._config_mutex); - _parentConfig().invert = invert; - - _parent._mask |= hidpp20::HiresScroll::Mode::Inverted; - if (invert) - _parent._mode |= hidpp20::HiresScroll::Mode::Inverted; - else - _parent._mode &= ~hidpp20::HiresScroll::Mode::Inverted; - - _parent._configure(); -} - -void HiresScroll::IPC::setTarget(bool target) { - std::unique_lock lock(_parent._config_mutex); - _parentConfig().target = target; - - _parent._mask |= hidpp20::HiresScroll::Mode::Target; - if (target) - _parent._mode |= hidpp20::HiresScroll::Mode::Target; - else - _parent._mode &= ~hidpp20::HiresScroll::Mode::Target; - - _parent._configure(); -} - -void HiresScroll::IPC::setUp(const std::string& type) { - std::unique_lock lock(_parent._config_mutex); - - auto& config = _parentConfig(); - - if (!config.up.has_value()) { - config.up = config::NoGesture(); - } - _parent._up_gesture = actions::Gesture::makeGesture( - _parent._device, type, config.up.value(), _parent._up_node); - if (!_parent._up_gesture->wheelCompatibility()) { - _parent._up_node.reset(); - config.up.reset(); - - throw std::invalid_argument("incompatible gesture"); - } else { - _parent._fixGesture(_parent._up_gesture); - } -} - -void HiresScroll::IPC::setDown(const std::string& type) { - std::unique_lock lock(_parent._config_mutex); - - auto& config = _parentConfig(); - - if (!config.down.has_value()) { - config.down = config::NoGesture(); - } - _parent._down_gesture = actions::Gesture::makeGesture( - _parent._device, type, config.down.value(), _parent._down_node); - if (!_parent._down_gesture->wheelCompatibility()) { - _parent._down_node.reset(); - config.down.reset(); - - throw std::invalid_argument("incompatible gesture"); - } else { - _parent._fixGesture(_parent._down_gesture); - } -} +/* + * Copyright 2019-2023 PixlOne + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * 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 + +using namespace logid; +using namespace logid::features; +using namespace logid::backend; + +HiresScroll::HiresScroll(Device* dev) : + DeviceFeature(dev), + _config(dev->activeProfile().hiresscroll), _mode(0), + _mask(0), + _node(dev->ipcNode()->make_child("hires_scroll")), + _up_node(_node->make_child("up")), + _down_node(_node->make_child("down")) { + + try { + _hires_scroll = std::make_shared(&dev->hidpp20()); + } catch (hidpp20::UnsupportedFeature& e) { + throw UnsupportedFeature(); + } + + _makeConfig(); + + _last_scroll = std::chrono::system_clock::now(); + + _ipc_interface = dev->ipcNode()->make_interface(this); +} + +void HiresScroll::_makeConfig() { + auto& config = _config.get(); + _mode = 0; + _mask = 0; + + if (config.has_value()) { + if (std::holds_alternative(config.value())) { + config::HiresScroll conf{}; + conf.hires = std::get(config.value()); + conf.invert = false; + conf.target = false; + _mask |= hidpp20::HiresScroll::Mode::HiRes; + config.value() = conf; + } + auto& conf = std::get(config.value()); + if (conf.hires.has_value()) { + _mask |= hidpp20::HiresScroll::Mode::HiRes; + if (conf.hires.value()) + _mode |= hidpp20::HiresScroll::Mode::HiRes; + } + if (conf.invert.has_value()) { + _mask |= hidpp20::HiresScroll::Mode::Inverted; + if (conf.invert.value()) + _mode |= hidpp20::HiresScroll::Mode::Inverted; + } + if (conf.target.has_value()) { + _mask |= hidpp20::HiresScroll::Mode::Target; + if (conf.target.value()) + _mode |= hidpp20::HiresScroll::Mode::Target; + } + + _makeGesture(_up_gesture, conf.up, "up"); + _makeGesture(_down_gesture, conf.down, "down"); + } +} + +void HiresScroll::configure() { + std::shared_lock lock(_config_mutex); + _configure(); +} + +void HiresScroll::_configure() { + auto mode = _hires_scroll->getMode(); + mode &= ~_mask; + mode |= (_mode & _mask); + _hires_scroll->setMode(mode); +} + +void HiresScroll::listen() { + std::shared_lock lock(_config_mutex); + if (_ev_handler.empty()) { + _ev_handler = _device->hidpp20().addEventHandler( + {[index = _hires_scroll->featureIndex()]( + const hidpp::Report& report) -> bool { + return (report.feature() == index) && + (report.function() == hidpp20::HiresScroll::WheelMovement); + }, + [self_weak = self()](const hidpp::Report& report) { + if (auto self = self_weak.lock()) + self->_handleScroll(self->_hires_scroll->wheelMovementEvent(report)); + } + }); + } +} + +void HiresScroll::setProfile(config::Profile& profile) { + std::unique_lock lock(_config_mutex); + + _up_gesture.reset(); + _down_gesture.reset(); + _config = profile.hiresscroll; + _makeConfig(); +} + +uint8_t HiresScroll::getMode() { + return _hires_scroll->getMode(); +} + +void HiresScroll::setMode(uint8_t mode) { + _hires_scroll->setMode(mode); +} + +void HiresScroll::_makeGesture(std::shared_ptr& gesture, + std::optional& config, + const std::string& direction) { + if (config.has_value()) { + gesture = actions::Gesture::makeGesture(_device, config.value(), + _node->make_child(direction)); + + _fixGesture(gesture); + } else { + gesture.reset(); + } +} + +void HiresScroll::_fixGesture(const std::shared_ptr& gesture) { + try { + auto axis = std::dynamic_pointer_cast(gesture); + if (axis) + axis->setHiresMultiplier(_hires_scroll->getCapabilities().multiplier); + } catch (std::bad_cast& e) {} + if (gesture) + gesture->press(true); +} + +void HiresScroll::_handleScroll(hidpp20::HiresScroll::WheelStatus event) { + std::shared_lock lock(_config_mutex); + auto now = std::chrono::system_clock::now(); + if (std::chrono::duration_cast(now - _last_scroll).count() >= 1) { + if (_up_gesture) { + _up_gesture->release(false); + _up_gesture->press(true); + } + if (_down_gesture) { + _down_gesture->release(false); + _down_gesture->press(true); + } + + _last_direction = 0; + } + + if (event.deltaV > 0) { + if (_last_direction == -1) { + if (_down_gesture) { + _down_gesture->release(false); + _down_gesture->press(true); + } + } + if (_up_gesture) + _up_gesture->move(event.deltaV); + _last_direction = 1; + } else if (event.deltaV < 0) { + if (_last_direction == 1) { + if (_up_gesture) { + _up_gesture->release(false); + _up_gesture->press(true); + } + } + if (_down_gesture) + _down_gesture->move((int16_t) -event.deltaV); + _last_direction = -1; + } + + _last_scroll = now; +} + +HiresScroll::IPC::IPC(HiresScroll* parent) : ipcgull::interface( + SERVICE_ROOT_NAME ".HiresScroll", { + {"GetConfig", {this, &IPC::getConfig, {"hires", "invert", "target"}}}, + {"SetHires", {this, &IPC::setHires, {"hires"}}}, + {"SetInvert", {this, &IPC::setInvert, {"invert"}}}, + {"SetTarget", {this, &IPC::setTarget, {"target"}}}, + {"SetUp", {this, &IPC::setUp, {"type"}}}, + {"SetDown", {this, &IPC::setDown, {"type"}}}, + }, {}, {}), _parent(*parent) { +} + +std::tuple HiresScroll::IPC::getConfig() const { + std::shared_lock lock(_parent._config_mutex); + + auto& config = _parent._config.get(); + + if (config.has_value()) { + if (std::holds_alternative(config.value())) { + return {std::get(config.value()), false, false}; + } else { + const auto& config_obj = std::get(config.value()); + return { + config_obj.hires.value_or(true), + config_obj.invert.value_or(false), + config_obj.target.value_or(false) + }; + } + } else { + return {true, false, false}; + } +} + +config::HiresScroll& HiresScroll::IPC::_parentConfig() { + auto& config = _parent._config.get(); + if (!config.has_value()) { + config = config::HiresScroll(); + } else if (std::holds_alternative(config.value())) { + bool hires = std::get(config.value()); + auto new_config = config::HiresScroll(); + new_config.hires = hires; + config = new_config; + } + + return std::get(config.value()); +} + +void HiresScroll::IPC::setHires(bool hires) { + std::unique_lock lock(_parent._config_mutex); + _parentConfig().hires = hires; + + _parent._mask |= hidpp20::HiresScroll::Mode::HiRes; + if (hires) + _parent._mode |= hidpp20::HiresScroll::Mode::HiRes; + else + _parent._mode &= ~hidpp20::HiresScroll::Mode::HiRes; + + _parent._configure(); +} + +void HiresScroll::IPC::setInvert(bool invert) { + std::unique_lock lock(_parent._config_mutex); + _parentConfig().invert = invert; + + _parent._mask |= hidpp20::HiresScroll::Mode::Inverted; + if (invert) + _parent._mode |= hidpp20::HiresScroll::Mode::Inverted; + else + _parent._mode &= ~hidpp20::HiresScroll::Mode::Inverted; + + _parent._configure(); +} + +void HiresScroll::IPC::setTarget(bool target) { + std::unique_lock lock(_parent._config_mutex); + _parentConfig().target = target; + + _parent._mask |= hidpp20::HiresScroll::Mode::Target; + if (target) + _parent._mode |= hidpp20::HiresScroll::Mode::Target; + else + _parent._mode &= ~hidpp20::HiresScroll::Mode::Target; + + _parent._configure(); +} + +void HiresScroll::IPC::setUp(const std::string& type) { + std::unique_lock lock(_parent._config_mutex); + + auto& config = _parentConfig(); + + if (!config.up.has_value()) { + config.up = config::NoGesture(); + } + _parent._up_gesture = actions::Gesture::makeGesture( + _parent._device, type, config.up.value(), _parent._up_node); + if (!_parent._up_gesture->wheelCompatibility()) { + _parent._up_node.reset(); + config.up.reset(); + + throw std::invalid_argument("incompatible gesture"); + } else { + _parent._fixGesture(_parent._up_gesture); + } +} + +void HiresScroll::IPC::setDown(const std::string& type) { + std::unique_lock lock(_parent._config_mutex); + + auto& config = _parentConfig(); + + if (!config.down.has_value()) { + config.down = config::NoGesture(); + } + _parent._down_gesture = actions::Gesture::makeGesture( + _parent._device, type, config.down.value(), _parent._down_node); + if (!_parent._down_gesture->wheelCompatibility()) { + _parent._down_node.reset(); + config.down.reset(); + + throw std::invalid_argument("incompatible gesture"); + } else { + _parent._fixGesture(_parent._down_gesture); + } +} diff --git a/src/logid/features/HiresScroll.h b/src/logid/features/HiresScroll.h index 29b8caa..baf69ed 100644 --- a/src/logid/features/HiresScroll.h +++ b/src/logid/features/HiresScroll.h @@ -1,104 +1,104 @@ -/* - * Copyright 2019-2023 PixlOne - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * 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 -#include -#include -#include -#include -#include -#include -#include - -namespace logid::features { - class HiresScroll : public DeviceFeature { - public: - void configure() final; - - void listen() final; - - void setProfile(config::Profile& profile) final; - - [[nodiscard]] uint8_t getMode(); - - void setMode(uint8_t mode); - - protected: - explicit HiresScroll(Device* dev); - - private: - void _makeConfig(); - - EventHandlerLock _ev_handler; - - void _makeGesture(std::shared_ptr& gesture, - std::optional& config, - const std::string& direction); - - void _configure(); - - void _fixGesture(const std::shared_ptr& gesture); - - void _handleScroll(backend::hidpp20::HiresScroll::WheelStatus event); - - class IPC : public ipcgull::interface { - public: - explicit IPC(HiresScroll* parent); - - [[nodiscard]] std::tuple getConfig() const; - - void setHires(bool hires); - - void setInvert(bool invert); - - void setTarget(bool target); - - void setUp(const std::string& type); - - void setDown(const std::string& type); - - private: - config::HiresScroll& _parentConfig(); - - HiresScroll& _parent; - }; - - std::shared_ptr _hires_scroll; - std::chrono::time_point _last_scroll; - int16_t _last_direction = 0; - - mutable std::shared_mutex _config_mutex; - std::reference_wrapper>> _config; - - uint8_t _mode; - uint8_t _mask; - - std::shared_ptr _up_gesture; - std::shared_ptr _down_gesture; - - std::shared_ptr _node; - std::shared_ptr _up_node; - std::shared_ptr _down_node; - - std::shared_ptr _ipc_interface; - }; -} - -#endif //LOGID_FEATURE_HIRESSCROLL_H +/* + * Copyright 2019-2023 PixlOne + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * 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 +#include +#include +#include +#include +#include +#include +#include + +namespace logid::features { + class HiresScroll : public DeviceFeature { + public: + void configure() final; + + void listen() final; + + void setProfile(config::Profile& profile) final; + + [[nodiscard]] uint8_t getMode(); + + void setMode(uint8_t mode); + + protected: + explicit HiresScroll(Device* dev); + + private: + void _makeConfig(); + + EventHandlerLock _ev_handler; + + void _makeGesture(std::shared_ptr& gesture, + std::optional& config, + const std::string& direction); + + void _configure(); + + void _fixGesture(const std::shared_ptr& gesture); + + void _handleScroll(backend::hidpp20::HiresScroll::WheelStatus event); + + class IPC : public ipcgull::interface { + public: + explicit IPC(HiresScroll* parent); + + [[nodiscard]] std::tuple getConfig() const; + + void setHires(bool hires); + + void setInvert(bool invert); + + void setTarget(bool target); + + void setUp(const std::string& type); + + void setDown(const std::string& type); + + private: + config::HiresScroll& _parentConfig(); + + HiresScroll& _parent; + }; + + std::shared_ptr _hires_scroll; + std::chrono::time_point _last_scroll; + int16_t _last_direction = 0; + + mutable std::shared_mutex _config_mutex; + std::reference_wrapper>> _config; + + uint8_t _mode; + uint8_t _mask; + + std::shared_ptr _up_gesture; + std::shared_ptr _down_gesture; + + std::shared_ptr _node; + std::shared_ptr _up_node; + std::shared_ptr _down_node; + + std::shared_ptr _ipc_interface; + }; +} + +#endif //LOGID_FEATURE_HIRESSCROLL_H diff --git a/src/logid/features/RemapButton.cpp b/src/logid/features/RemapButton.cpp index 0d6dd9a..638bce8 100644 --- a/src/logid/features/RemapButton.cpp +++ b/src/logid/features/RemapButton.cpp @@ -1,316 +1,316 @@ -/* - * Copyright 2019-2023 PixlOne - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * 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 - -using namespace logid::features; -using namespace logid::backend; -using namespace logid::actions; - -#define REPROG_FLAG(x) (control.second.flags & hidpp20::ReprogControls::x ? "YES" : "") -#define REPROG_FLAG_ADDITIONAL(x) (control.second.additionalFlags & \ - hidpp20::ReprogControls::x ? "YES" : "") - -static constexpr auto hidpp20_reprog_rebind = - (hidpp20::ReprogControls::ChangeTemporaryDivert | - hidpp20::ReprogControls::ChangeRawXYDivert); - -RemapButton::RemapButton(Device* dev) : DeviceFeature(dev), - _config(dev->activeProfile().buttons), - _ipc_node(dev->ipcNode()->make_child("buttons")) { - try { - _reprog_controls = hidpp20::ReprogControls::autoVersion( - &dev->hidpp20()); - } catch (hidpp20::UnsupportedFeature& e) { - throw UnsupportedFeature(); - } - - _reprog_controls->initCidMap(); - - auto& config = _config.get(); - - if (!config.has_value()) - config = config::RemapButton(); - - for (const auto& control: _reprog_controls->getControls()) { - const auto i = _buttons.size(); - Button::ConfigFunction func = [this, info = control.second]( - const std::shared_ptr& action) { - hidpp20::ReprogControls::ControlInfo report{}; - report.controlID = info.controlID; - report.flags = hidpp20_reprog_rebind; - - if (action) { - if ((action->reprogFlags() & hidpp20::ReprogControls::RawXYDiverted) && - (!_reprog_controls->supportsRawXY() || - !(info.additionalFlags & hidpp20::ReprogControls::RawXY))) - logPrintf(WARN, "%s: 'Cannot divert raw XY movements for CID 0x%02x", - _device->name().c_str(), info.controlID); - - report.flags |= action->reprogFlags(); - } - _reprog_controls->setControlReporting(info.controlID, report); - }; - _buttons.emplace(control.second.controlID, - Button::make(control.second, (int) i, - _device, func, _ipc_node, - config.value()[control.first])); - } - - _ipc_interface = _device->ipcNode()->make_interface(this); - - if (global_loglevel <= DEBUG) { - // 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, REPROG_FLAG(TemporaryDivertable), REPROG_FLAG(FKey), - REPROG_FLAG(MouseButton), REPROG_FLAG_ADDITIONAL(RawXY)); - } -} - -void RemapButton::configure() { - for (const auto& button: _buttons) - button.second->configure(); -} - -void RemapButton::listen() { - if (_ev_handler.empty()) { - _ev_handler = _device->hidpp20().addEventHandler( - {[index = _reprog_controls->featureIndex()]( - const hidpp::Report& report) -> bool { - if (report.feature() == index) { - switch (report.function()) { - case hidpp20::ReprogControls::DivertedButtonEvent: - case hidpp20::ReprogControls::DivertedRawXYEvent: - return true; - default: - return false; - } - } - return false; - }, - [self_weak = self()](const hidpp::Report& report) -> void { - auto self = self_weak.lock(); - if (!self) - return; - - switch (report.function()) { - case hidpp20::ReprogControls::DivertedButtonEvent: - self->_buttonEvent( - self->_reprog_controls->divertedButtonEvent(report)); - break; - case hidpp20::ReprogControls::DivertedRawXYEvent: { - auto divertedXY = self->_reprog_controls->divertedRawXYEvent(report); - for (const auto& button: self->_buttons) - if (button.second->pressed()) - button.second->move(divertedXY.x, divertedXY.y); - break; - } - default: - break; - } - } - }); - } -} - -void RemapButton::setProfile(config::Profile& profile) { - std::lock_guard lock(_button_lock); - - _config = profile.buttons; - if (!_config.get().has_value()) - _config.get().emplace(); - auto& config = _config.get().value(); - - for(auto& button : _buttons) - button.second->setProfile(config[button.first]); -} - -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 (const 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 = _buttons.find(i); - if (action != _buttons.end()) - action->second->press(); - } - } - - // Release all removed buttons - for (auto& i: _pressed_buttons) { - auto action = _buttons.find(i); - if (action != _buttons.end()) - action->second->release(); - } - - _pressed_buttons = new_state; -} - -namespace logid::features { - class ButtonWrapper : public Button { - public: - template - explicit ButtonWrapper(Args&& ... args) : Button(std::forward(args)...) { - } - }; -} - -std::shared_ptr