mirror of
https://github.com/PixlOne/logiops.git
synced 2025-09-14 13:56:50 +08:00
Compare commits
53 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
628ab937a2 | ||
![]() |
9495516e0c | ||
![]() |
237fa5fbd3 | ||
![]() |
456efb4db0 | ||
![]() |
d79d050bf4 | ||
![]() |
e9f8072a0c | ||
![]() |
da742af3a5 | ||
![]() |
700070c651 | ||
![]() |
94f6dbab53 | ||
![]() |
f2680bdf5f | ||
![]() |
d51fd5c344 | ||
![]() |
31d1955faf | ||
![]() |
cb7a2dad7c | ||
![]() |
5767aac362 | ||
![]() |
64962a8ed3 | ||
![]() |
b4a9d4460e | ||
![]() |
b81f935bcd | ||
![]() |
5e32120b2c | ||
![]() |
be5ee9f793 | ||
![]() |
a361f206ff | ||
![]() |
f85cd5ba62 | ||
![]() |
4ae58b81a3 | ||
![]() |
30ade71edf | ||
![]() |
eb5b3ca481 | ||
![]() |
be840b333a | ||
![]() |
99716cbd99 | ||
![]() |
3fb18a7d5f | ||
![]() |
1503a1b2ca | ||
![]() |
a77b328b35 | ||
![]() |
4e70095281 | ||
![]() |
1d7ff5e034 | ||
![]() |
1267db027c | ||
![]() |
bb8e0b4a78 | ||
![]() |
a96036c97d | ||
![]() |
5e436a2bdf | ||
![]() |
5547f52cad | ||
![]() |
2815fc50f8 | ||
![]() |
2df4351ff8 | ||
![]() |
0945fa1fe8 | ||
![]() |
4c406c7363 | ||
![]() |
7147825539 | ||
![]() |
c27a6edae8 | ||
![]() |
9b020a9f9c | ||
![]() |
9cf7e438cd | ||
![]() |
e238dce6f1 | ||
![]() |
44e319d770 | ||
![]() |
c5a9c1d0a4 | ||
![]() |
c88f8b9b53 | ||
![]() |
fbd915e201 | ||
![]() |
cdca3e8312 | ||
![]() |
0ca528a91f | ||
![]() |
cbee607902 | ||
![]() |
5741a9a323 |
55
.github/workflows/build-test.yml
vendored
Normal file
55
.github/workflows/build-test.yml
vendored
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
name: Build test
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
pull_request:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
name: Build
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
container: ${{ matrix.container }}
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
container: [ 'ubuntu:latest', 'ubuntu:20.04', 'fedora:latest', 'archlinux:base-devel' ]
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Install dependencies (Ubuntu)
|
||||||
|
if: startsWith(matrix.container, 'ubuntu')
|
||||||
|
env:
|
||||||
|
DEBIAN_FRONTEND: noninteractive
|
||||||
|
TZ: Etc/UTC
|
||||||
|
run: |
|
||||||
|
apt-get update -y -q
|
||||||
|
apt-get install -y -q \
|
||||||
|
build-essential cmake git pkg-config \
|
||||||
|
libevdev-dev libudev-dev libconfig++-dev libglib2.0-dev
|
||||||
|
|
||||||
|
- name: Install dependencies (Fedora)
|
||||||
|
if: startsWith(matrix.container, 'fedora')
|
||||||
|
run: |
|
||||||
|
dnf update -y
|
||||||
|
dnf install -y \
|
||||||
|
cmake git libevdev-devel \
|
||||||
|
systemd-devel libconfig-devel gcc-c++ glib2-devel
|
||||||
|
|
||||||
|
- name: Install dependencies (Arch Linux)
|
||||||
|
if: startsWith(matrix.container, 'archlinux')
|
||||||
|
run: |
|
||||||
|
pacman -Syu --noconfirm \
|
||||||
|
cmake git libevdev libconfig systemd-libs glib2
|
||||||
|
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
with:
|
||||||
|
submodules: recursive
|
||||||
|
|
||||||
|
- name: Build LogiOps
|
||||||
|
run: |
|
||||||
|
cmake -B build \
|
||||||
|
-DCMAKE_BUILD_TYPE=Release \
|
||||||
|
-DCMAKE_CXX_FLAGS="-Werror"
|
||||||
|
cmake --build build -j$(nproc)
|
35
.github/workflows/make-release.yml
vendored
Normal file
35
.github/workflows/make-release.yml
vendored
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
name: Publish release tarball
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
tags:
|
||||||
|
- 'v*.*'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
publish:
|
||||||
|
name: Publish release tarball
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
env:
|
||||||
|
ARCHIVE_NAME: logiops-${{ github.ref_name }}
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
with:
|
||||||
|
submodules: recursive
|
||||||
|
|
||||||
|
- name: Add version info
|
||||||
|
run: echo ${{ github.ref_name }} > version.txt
|
||||||
|
|
||||||
|
- name: Remove git repo info
|
||||||
|
run: find . -name '.git' | xargs rm -rf
|
||||||
|
|
||||||
|
- name: Create tarball
|
||||||
|
run: |
|
||||||
|
find * -type f| tar caf /tmp/$ARCHIVE_NAME.tar.gz \
|
||||||
|
--xform s:^:$ARCHIVE_NAME/: --verbatim-files-from -T-
|
||||||
|
mv /tmp/$ARCHIVE_NAME.tar.gz .
|
||||||
|
|
||||||
|
- name: Upload release asset
|
||||||
|
uses: softprops/action-gh-release@v0.1.15
|
||||||
|
with:
|
||||||
|
files: logiops-${{ github.ref_name }}.tar.gz
|
37
.github/workflows/release.yml
vendored
37
.github/workflows/release.yml
vendored
@ -1,37 +0,0 @@
|
|||||||
name: release-ci
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
types:
|
|
||||||
- tags
|
|
||||||
workflow_dispatch:
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
job:
|
|
||||||
name: ${{ matrix.os }}-release
|
|
||||||
runs-on: ${{ matrix.os }}
|
|
||||||
strategy:
|
|
||||||
fail-fast: false
|
|
||||||
matrix:
|
|
||||||
os: [ubuntu-latest]
|
|
||||||
include:
|
|
||||||
- os: ubuntu-latest
|
|
||||||
triplet: x64-linux
|
|
||||||
installDependencies: 'sudo apt-get update -m && sudo apt-get install libconfig++-dev libevdev-dev libudev-dev'
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v1
|
|
||||||
with:
|
|
||||||
submodules: true
|
|
||||||
- name: Install dependencies
|
|
||||||
run: '${{ matrix.installDependencies }}'
|
|
||||||
- name: Run CMake+Make
|
|
||||||
uses: lukka/run-cmake@v2
|
|
||||||
id: runcmake
|
|
||||||
with:
|
|
||||||
cmakeGenerator: 'UnixMakefiles'
|
|
||||||
cmakeListsOrSettingsJson: 'CMakeListsTxtBasic'
|
|
||||||
cmakeListsTxtPath: '${{ github.workspace }}/CMakeLists.txt'
|
|
||||||
cmakeAdditionalArgs: '-DFORCE_BUILD_HIDPP=True'
|
|
||||||
buildWithCMakeArgs: '-- -v'
|
|
||||||
cmakeBuildType: 'Release'
|
|
||||||
buildDirectory: '${{ runner.workspace }}/build/'
|
|
2
.gitmodules
vendored
2
.gitmodules
vendored
@ -1,3 +1,3 @@
|
|||||||
[submodule "src/ipcgull"]
|
[submodule "src/ipcgull"]
|
||||||
path = src/ipcgull
|
path = src/ipcgull
|
||||||
url = git@github.com:PixlOne/ipcgull.git
|
url = https://github.com/PixlOne/ipcgull.git
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
cmake_minimum_required(VERSION 3.10)
|
cmake_minimum_required(VERSION 3.12)
|
||||||
|
|
||||||
set(CMAKE_INSTALL_PREFIX /usr)
|
set(CMAKE_INSTALL_PREFIX /usr)
|
||||||
|
|
||||||
@ -6,6 +6,7 @@ project(logiops)
|
|||||||
|
|
||||||
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
||||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra")
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra")
|
||||||
|
set(CMAKE_CXX_FLAGS_NONE "${CMAKE_CXX_FLAGS_NONE} -DNDEBUG")
|
||||||
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -O0 -Wall -Wextra")
|
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -O0 -Wall -Wextra")
|
||||||
|
|
||||||
set(CMAKE_CXX_STANDARD 20)
|
set(CMAKE_CXX_STANDARD 20)
|
||||||
@ -13,13 +14,14 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
|||||||
|
|
||||||
option(USE_USER_BUS "Uses user bus" OFF)
|
option(USE_USER_BUS "Uses user bus" OFF)
|
||||||
|
|
||||||
find_package(Git REQUIRED)
|
find_package(Git)
|
||||||
|
|
||||||
# Set version number and update submodules
|
# Set version number and update submodules
|
||||||
if(EXISTS ${CMAKE_SOURCE_DIR}/.git)
|
if(EXISTS ${PROJECT_SOURCE_DIR}/.git AND GIT_FOUND)
|
||||||
execute_process(COMMAND ${GIT_EXECUTABLE} describe --tags
|
execute_process(COMMAND ${GIT_EXECUTABLE} describe --tags
|
||||||
OUTPUT_VARIABLE LOGIOPS_VERSION
|
OUTPUT_VARIABLE LOGIOPS_VERSION
|
||||||
RESULT_VARIABLE LOGIOPS_VERSION_RET
|
RESULT_VARIABLE LOGIOPS_VERSION_RET
|
||||||
|
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
|
||||||
ERROR_QUIET)
|
ERROR_QUIET)
|
||||||
if(NOT LOGIOPS_VERSION_RET EQUAL "0")
|
if(NOT LOGIOPS_VERSION_RET EQUAL "0")
|
||||||
execute_process(COMMAND ${GIT_EXECUTABLE}
|
execute_process(COMMAND ${GIT_EXECUTABLE}
|
||||||
@ -29,16 +31,18 @@ if(EXISTS ${CMAKE_SOURCE_DIR}/.git)
|
|||||||
endif()
|
endif()
|
||||||
|
|
||||||
execute_process(COMMAND ${GIT_EXECUTABLE}
|
execute_process(COMMAND ${GIT_EXECUTABLE}
|
||||||
submodule update --init --recursive)
|
submodule update --init --recursive
|
||||||
|
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR})
|
||||||
|
|
||||||
string(REGEX REPLACE "\n$" "" LOGIOPS_VERSION ${LOGIOPS_VERSION})
|
string(REGEX REPLACE "\n$" "" LOGIOPS_VERSION ${LOGIOPS_VERSION})
|
||||||
elseif(EXISTS ${CMAKE_SOURCE_DIR}/version.txt)
|
elseif(EXISTS ${PROJECT_SOURCE_DIR}/version.txt)
|
||||||
file(READ version.txt LOGIOPS_VERSION)
|
file(READ version.txt LOGIOPS_VERSION)
|
||||||
string(REGEX REPLACE "\n$" "" LOGIOPS_VERSION ${LOGIOPS_VERSION})
|
string(REGEX REPLACE "\n$" "" LOGIOPS_VERSION ${LOGIOPS_VERSION})
|
||||||
|
|
||||||
IF(NOT EXISTS src/ipcgull)
|
endif()
|
||||||
message(FATAL_ERROR "Missing ipcgull submodule")
|
|
||||||
endif()
|
IF(NOT EXISTS ${PROJECT_SOURCE_DIR}/src/ipcgull)
|
||||||
|
message(FATAL_ERROR "Missing ipcgull submodule")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(NOT LOGIOPS_VERSION)
|
if(NOT LOGIOPS_VERSION)
|
||||||
|
16
README.md
16
README.md
@ -1,4 +1,6 @@
|
|||||||
# logiops
|
# LogiOps
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
This is an unofficial driver for Logitech mice and keyboard.
|
This is an unofficial driver for Logitech mice and keyboard.
|
||||||
|
|
||||||
@ -13,20 +15,20 @@ Default location for the configuration file is /etc/logid.cfg, but another can b
|
|||||||
|
|
||||||
## Dependencies
|
## Dependencies
|
||||||
|
|
||||||
This project requires a C++17 compiler, `cmake`, `libevdev`, `libudev`, `glib`, and `libconfig`.
|
This project requires a C++20 compiler, `cmake`, `libevdev`, `libudev`, `glib`, and `libconfig`.
|
||||||
For popular distributions, I've included commands below.
|
For popular distributions, I've included commands below.
|
||||||
|
|
||||||
**Arch Linux:** `sudo pacman -S cmake libevdev libconfig pkgconf glib2`
|
**Arch Linux:** `sudo pacman -S base-devel cmake libevdev libconfig systemd-libs glib2`
|
||||||
|
|
||||||
**Debian/Ubuntu:** `sudo apt install cmake libevdev-dev libudev-dev libconfig++-dev libglib2.0`
|
**Debian/Ubuntu:** `sudo apt install build-essential cmake pkg-config libevdev-dev libudev-dev libconfig++-dev libglib2.0-dev`
|
||||||
|
|
||||||
**Fedora:** `sudo dnf install cmake libevdev-devel systemd-devel libconfig-devel gcc-c++ glib2`
|
**Fedora:** `sudo dnf install cmake libevdev-devel systemd-devel libconfig-devel gcc-c++ glib2-devel`
|
||||||
|
|
||||||
**Gentoo Linux:** `sudo emerge dev-libs/libconfig dev-libs/libevdev dev-libs/glib dev-util/cmake virtual/libudev`
|
**Gentoo Linux:** `sudo emerge dev-libs/libconfig dev-libs/libevdev dev-libs/glib dev-util/cmake virtual/libudev`
|
||||||
|
|
||||||
**Solus:** `sudo eopkg install libevdev-devel libconfig-devel libgudev-devel glib2`
|
**Solus:** `sudo eopkg install cmake libevdev-devel libconfig-devel libgudev-devel glib2-devel`
|
||||||
|
|
||||||
**openSUSE:** `sudo zypper install cmake libevdev-devel systemd-devel libconfig-devel gcc-c++ libconfig++-devel libudev-devel glib2`
|
**openSUSE:** `sudo zypper install cmake libevdev-devel systemd-devel libconfig-devel gcc-c++ libconfig++-devel libudev-devel glib2-devel`
|
||||||
|
|
||||||
## Building
|
## Building
|
||||||
|
|
||||||
|
32
TESTED.md
32
TESTED.md
@ -2,17 +2,21 @@
|
|||||||
|
|
||||||
This is not by any means an exhaustive list. Many more devices are supported but these devices are the ones that are confirmed to be working. To add to this list, submit a pull request adding your device.
|
This is not by any means an exhaustive list. Many more devices are supported but these devices are the ones that are confirmed to be working. To add to this list, submit a pull request adding your device.
|
||||||
|
|
||||||
| Device | Compatible? | Config Name |
|
|
||||||
| :------------: | :---------: | :------------------------------------: |
|
| Device | Compatible? | Config Name |
|
||||||
| MX Master 3 | Yes | `Wireless Mouse MX Master 3` |
|
| :-----------------: | :---------: | :------------------------------------: |
|
||||||
| MX Master 2S | Yes | `Wireless Mouse MX Master 2S` |
|
| MX Master 3S | Yes | `MX Master 3S` |
|
||||||
| MX Master | Yes | `Wireless Mouse MX Master` |
|
| MX Master 3 | Yes | `Wireless Mouse MX Master 3` |
|
||||||
| MX Anywhere S2 | Yes | `Wireless Mobile Mouse MX Anywhere 2S` |
|
| MX Master 3 for Mac | Yes | `MX Master 3 for Mac` |
|
||||||
| MX Anywhere 3 | Yes | `MX Anywhere 3` |
|
| MX Master 2S | Yes | `Wireless Mouse MX Master 2S` |
|
||||||
| MX Vertical | Yes | `MX Vertical Advanced Ergonomic Mouse` |
|
| MX Master | Yes | `Wireless Mouse MX Master` |
|
||||||
| MX Ergo | Yes | `MX Ergo Multi-Device Trackball ` |
|
| MX Anywhere S2 | Yes | `Wireless Mobile Mouse MX Anywhere 2S` |
|
||||||
| M720 | Yes | `M720 Triathlon Multi-Device Mouse` |
|
| MX Anywhere 3 | Yes | `MX Anywhere 3` |
|
||||||
| M590 | Yes | `M585/M590 Multi-Device Mouse` |
|
| MX Vertical | Yes | `MX Vertical Advanced Ergonomic Mouse` |
|
||||||
| T400 | Yes | `Zone Touch Mouse T400` |
|
| MX Ergo | Yes | `MX Ergo Multi-Device Trackball ` |
|
||||||
| MX Keys | Yes | `MX Keys Wireless Keyboard` |
|
| MX Ergo M575 | Yes | `ERGO M575 Trackball` |
|
||||||
| M500s | Yes | `Advanced Corded Mouse M500s` |
|
| M720 | Yes | `M720 Triathlon Multi-Device Mouse` |
|
||||||
|
| M590 | Yes | `M585/M590 Multi-Device Mouse` |
|
||||||
|
| T400 | Yes | `Zone Touch Mouse T400` |
|
||||||
|
| MX Keys | Yes | `MX Keys Wireless Keyboard` |
|
||||||
|
| M500s | Yes | `Advanced Corded Mouse M500s` |
|
||||||
|
@ -5,6 +5,7 @@ devices: (
|
|||||||
{
|
{
|
||||||
on: true;
|
on: true;
|
||||||
threshold: 30;
|
threshold: 30;
|
||||||
|
torque: 50;
|
||||||
};
|
};
|
||||||
hiresscroll:
|
hiresscroll:
|
||||||
{
|
{
|
||||||
|
@ -1 +1 @@
|
|||||||
Subproject commit 3b19a11e8adf5608c9facd6a4e93d8833cbc4e3f
|
Subproject commit cd0f9a8cefb5b2545e163fceb249fdbcbaf666aa
|
@ -1,8 +1,9 @@
|
|||||||
cmake_minimum_required(VERSION 3.10)
|
cmake_minimum_required(VERSION 3.12)
|
||||||
project(logid)
|
project(logid)
|
||||||
|
|
||||||
# C++20 is only needed for string literal template parameters
|
# C++20 is only needed for string literal template parameters
|
||||||
set(CMAKE_CXX_STANDARD 20)
|
set(CMAKE_CXX_STANDARD 20)
|
||||||
|
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||||
|
|
||||||
set(CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/../CMake")
|
set(CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/../CMake")
|
||||||
|
|
||||||
@ -12,7 +13,7 @@ find_package(PkgConfig REQUIRED)
|
|||||||
add_executable(logid
|
add_executable(logid
|
||||||
logid.cpp
|
logid.cpp
|
||||||
util/log.cpp
|
util/log.cpp
|
||||||
config/util.cpp
|
config/config.cpp
|
||||||
InputDevice.cpp
|
InputDevice.cpp
|
||||||
DeviceManager.cpp
|
DeviceManager.cpp
|
||||||
Device.cpp
|
Device.cpp
|
||||||
@ -71,7 +72,6 @@ add_executable(logid
|
|||||||
set_target_properties(logid PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR})
|
set_target_properties(logid PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR})
|
||||||
|
|
||||||
pkg_check_modules(PC_EVDEV libevdev REQUIRED)
|
pkg_check_modules(PC_EVDEV libevdev REQUIRED)
|
||||||
pkg_check_modules(DBUS "dbus-1")
|
|
||||||
pkg_check_modules(SYSTEMD "systemd")
|
pkg_check_modules(SYSTEMD "systemd")
|
||||||
pkg_check_modules(LIBCONFIG libconfig REQUIRED)
|
pkg_check_modules(LIBCONFIG libconfig REQUIRED)
|
||||||
pkg_check_modules(LIBUDEV libudev REQUIRED)
|
pkg_check_modules(LIBUDEV libudev REQUIRED)
|
||||||
@ -110,13 +110,11 @@ 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.")
|
message(FATAL_ERROR "systemd is not found w/ pkg-config but SYSTEMD_SERVICES_INSTALL_DIR is defined.")
|
||||||
endif ()
|
endif ()
|
||||||
|
|
||||||
if(DBUS_FOUND)
|
# Install DBus conf
|
||||||
# Install DBus conf
|
# TODO: Is there a better way of setting the system policy directory?
|
||||||
# TODO: Is there a better way of setting the system policy directory?
|
set(DBUS_SYSTEM_POLICY_INSTALL_DIR "/usr/share/dbus-1/system.d")
|
||||||
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)
|
||||||
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}")
|
||||||
message(STATUS "dbus system policy will be installed at ${DBUS_SYSTEM_POLICY_INSTALL_DIR}")
|
install(FILES ${CMAKE_BINARY_DIR}/pizza.pixl.LogiOps.conf
|
||||||
install(FILES ${CMAKE_BINARY_DIR}/pizza.pixl.LogiOps.conf
|
DESTINATION ${DBUS_SYSTEM_POLICY_INSTALL_DIR}
|
||||||
DESTINATION ${DBUS_SYSTEM_POLICY_INSTALL_DIR}
|
COMPONENT cp)
|
||||||
COMPONENT cp)
|
|
||||||
endif()
|
|
||||||
|
@ -19,6 +19,7 @@
|
|||||||
#include <Configuration.h>
|
#include <Configuration.h>
|
||||||
#include <util/log.h>
|
#include <util/log.h>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
#include <filesystem>
|
||||||
#include <ipc_defs.h>
|
#include <ipc_defs.h>
|
||||||
|
|
||||||
using namespace logid;
|
using namespace logid;
|
||||||
@ -27,19 +28,23 @@ using namespace logid::config;
|
|||||||
|
|
||||||
Configuration::Configuration(std::string config_file) :
|
Configuration::Configuration(std::string config_file) :
|
||||||
_config_file(std::move(config_file)) {
|
_config_file(std::move(config_file)) {
|
||||||
try {
|
if (std::filesystem::exists(_config_file)) {
|
||||||
_config.readFile(_config_file);
|
try {
|
||||||
} catch (const FileIOException& e) {
|
_config.readFile(_config_file.c_str());
|
||||||
logPrintf(ERROR, "I/O Error while reading %s: %s", _config_file.c_str(),
|
} catch (const FileIOException& e) {
|
||||||
|
logPrintf(ERROR, "I/O Error while reading %s: %s", _config_file.c_str(),
|
||||||
e.what());
|
e.what());
|
||||||
throw;
|
throw;
|
||||||
} catch (const ParseException& e) {
|
} catch (const ParseException& e) {
|
||||||
logPrintf(ERROR, "Parse error in %s, line %d: %s", e.getFile(),
|
logPrintf(ERROR, "Parse error in %s, line %d: %s", e.getFile(),
|
||||||
e.getLine(), e.getError());
|
e.getLine(), e.getError());
|
||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
|
|
||||||
Config::operator=(get<Config>(_config.getRoot()));
|
Config::operator=(get<Config>(_config.getRoot()));
|
||||||
|
} else {
|
||||||
|
logPrintf(INFO, "Config file does not exist, using empty config.");
|
||||||
|
}
|
||||||
|
|
||||||
if (!devices.has_value())
|
if (!devices.has_value())
|
||||||
devices.emplace();
|
devices.emplace();
|
||||||
@ -52,7 +57,7 @@ Configuration::Configuration() {
|
|||||||
void Configuration::save() {
|
void Configuration::save() {
|
||||||
config::set(_config.getRoot(), *this);
|
config::set(_config.getRoot(), *this);
|
||||||
try {
|
try {
|
||||||
_config.writeFile(_config_file);
|
_config.writeFile(_config_file.c_str());
|
||||||
} catch (const FileIOException& e) {
|
} catch (const FileIOException& e) {
|
||||||
logPrintf(ERROR, "I/O Error while writing %s: %s",
|
logPrintf(ERROR, "I/O Error while writing %s: %s",
|
||||||
_config_file.c_str(), e.what());
|
_config_file.c_str(), e.what());
|
||||||
@ -68,4 +73,4 @@ Configuration::IPC::IPC(Configuration* config) :
|
|||||||
ipcgull::interface(SERVICE_ROOT_NAME ".Config", {
|
ipcgull::interface(SERVICE_ROOT_NAME ".Config", {
|
||||||
{"Save", {config, &Configuration::save}}
|
{"Save", {config, &Configuration::save}}
|
||||||
}, {}, {}) {
|
}, {}, {}) {
|
||||||
}
|
}
|
||||||
|
@ -100,7 +100,6 @@ Device::Device(std::string path, backend::hidpp::DeviceIndex index,
|
|||||||
_path(std::move(path)), _index(index),
|
_path(std::move(path)), _index(index),
|
||||||
_config(_getConfig(manager, _hidpp20->name())),
|
_config(_getConfig(manager, _hidpp20->name())),
|
||||||
_profile_name(ipcgull::property_readable, ""),
|
_profile_name(ipcgull::property_readable, ""),
|
||||||
_receiver(nullptr),
|
|
||||||
_manager(manager),
|
_manager(manager),
|
||||||
_nickname(manager),
|
_nickname(manager),
|
||||||
_ipc_node(manager->devicesNode()->make_child(_nickname)),
|
_ipc_node(manager->devicesNode()->make_child(_nickname)),
|
||||||
@ -116,7 +115,6 @@ Device::Device(std::shared_ptr<backend::raw::RawDevice> raw_device,
|
|||||||
_path(raw_device->rawPath()), _index(index),
|
_path(raw_device->rawPath()), _index(index),
|
||||||
_config(_getConfig(manager, _hidpp20->name())),
|
_config(_getConfig(manager, _hidpp20->name())),
|
||||||
_profile_name(ipcgull::property_readable, ""),
|
_profile_name(ipcgull::property_readable, ""),
|
||||||
_receiver(nullptr),
|
|
||||||
_manager(manager),
|
_manager(manager),
|
||||||
_nickname(manager),
|
_nickname(manager),
|
||||||
_ipc_node(manager->devicesNode()->make_child(_nickname)),
|
_ipc_node(manager->devicesNode()->make_child(_nickname)),
|
||||||
@ -132,7 +130,6 @@ Device::Device(Receiver* receiver, hidpp::DeviceIndex index,
|
|||||||
_path(receiver->path()), _index(index),
|
_path(receiver->path()), _index(index),
|
||||||
_config(_getConfig(manager, _hidpp20->name())),
|
_config(_getConfig(manager, _hidpp20->name())),
|
||||||
_profile_name(ipcgull::property_readable, ""),
|
_profile_name(ipcgull::property_readable, ""),
|
||||||
_receiver(receiver),
|
|
||||||
_manager(manager),
|
_manager(manager),
|
||||||
_nickname(manager),
|
_nickname(manager),
|
||||||
_ipc_node(manager->devicesNode()->make_child(_nickname)),
|
_ipc_node(manager->devicesNode()->make_child(_nickname)),
|
||||||
@ -187,7 +184,6 @@ void Device::sleep() {
|
|||||||
|
|
||||||
void Device::wakeup() {
|
void Device::wakeup() {
|
||||||
std::lock_guard<std::mutex> lock(_state_lock);
|
std::lock_guard<std::mutex> lock(_state_lock);
|
||||||
logPrintf(INFO, "%s:%d woke up.", _path.c_str(), _index);
|
|
||||||
|
|
||||||
reconfigure();
|
reconfigure();
|
||||||
|
|
||||||
@ -195,6 +191,8 @@ void Device::wakeup() {
|
|||||||
_awake = true;
|
_awake = true;
|
||||||
_ipc_interface->notifyStatus();
|
_ipc_interface->notifyStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
logPrintf(INFO, "%s:%d woke up.", _path.c_str(), _index);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Device::reconfigure() {
|
void Device::reconfigure() {
|
||||||
|
@ -157,7 +157,6 @@ namespace logid {
|
|||||||
ipcgull::property<std::string> _profile_name;
|
ipcgull::property<std::string> _profile_name;
|
||||||
std::map<std::string, config::Profile>::iterator _profile;
|
std::map<std::string, config::Profile>::iterator _profile;
|
||||||
|
|
||||||
Receiver* _receiver;
|
|
||||||
const std::weak_ptr<DeviceManager> _manager;
|
const std::weak_ptr<DeviceManager> _manager;
|
||||||
|
|
||||||
void _makeResetMechanism();
|
void _makeResetMechanism();
|
||||||
|
@ -66,11 +66,11 @@ void DeviceManager::addDevice(std::string path) {
|
|||||||
|
|
||||||
// Check if device is ignored before continuing
|
// Check if device is ignored before continuing
|
||||||
{
|
{
|
||||||
raw::RawDevice raw_dev(path, self<DeviceManager>().lock());
|
auto raw_dev = raw::RawDevice::make(path, self<DeviceManager>().lock());
|
||||||
if (config()->ignore.has_value() &&
|
if (config()->ignore.has_value() &&
|
||||||
config()->ignore.value().contains(raw_dev.productId())) {
|
config()->ignore.value().contains(raw_dev->productId())) {
|
||||||
logPrintf(DEBUG, "%s: Device 0x%04x ignored.",
|
logPrintf(DEBUG, "%s: Device 0x%04x ignored.",
|
||||||
path.c_str(), raw_dev.productId());
|
path.c_str(), raw_dev->productId());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -21,6 +21,7 @@
|
|||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <mutex>
|
||||||
|
|
||||||
extern "C"
|
extern "C"
|
||||||
{
|
{
|
||||||
|
@ -92,6 +92,12 @@ GestureAction::GestureAction(Device* dev, config::GestureAction& config,
|
|||||||
direction,Gesture::makeGesture(
|
direction,Gesture::makeGesture(
|
||||||
dev, x.second,
|
dev, x.second,
|
||||||
_node->make_child(fromDirection(direction))));
|
_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) {
|
} catch (std::invalid_argument& e) {
|
||||||
logPrintf(WARN, "%s is not a direction", x.first.c_str());
|
logPrintf(WARN, "%s is not a direction", x.first.c_str());
|
||||||
}
|
}
|
||||||
@ -122,7 +128,7 @@ void GestureAction::release() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (auto& gesture: _gestures) {
|
for (auto& gesture: _gestures) {
|
||||||
if (gesture.first == d)
|
if (gesture.first == d || gesture.first == None)
|
||||||
continue;
|
continue;
|
||||||
if (!threshold_met) {
|
if (!threshold_met) {
|
||||||
if (gesture.second->metThreshold()) {
|
if (gesture.second->metThreshold()) {
|
||||||
@ -130,21 +136,15 @@ void GestureAction::release() {
|
|||||||
// secondary one.
|
// secondary one.
|
||||||
threshold_met = true;
|
threshold_met = true;
|
||||||
gesture.second->release(true);
|
gesture.second->release(true);
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
gesture.second->release(false);
|
gesture.second->release(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!threshold_met) {
|
auto none_gesture = _gestures.find(None);
|
||||||
try {
|
if (none_gesture != _gestures.end()) {
|
||||||
auto none = _gestures.at(None);
|
none_gesture->second->release(!threshold_met);
|
||||||
if (none) {
|
|
||||||
none->press(false);
|
|
||||||
none->release(false);
|
|
||||||
}
|
|
||||||
} catch (std::out_of_range& e) {}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -239,15 +239,23 @@ void GestureAction::setGesture(const std::string& direction, const std::string&
|
|||||||
|
|
||||||
auto dir_name = fromDirection(d);
|
auto dir_name = fromDirection(d);
|
||||||
|
|
||||||
|
auto& gesture = _config.gestures.value()[dir_name];
|
||||||
|
|
||||||
_gestures[d].reset();
|
_gestures[d].reset();
|
||||||
try {
|
try {
|
||||||
_gestures[d] = Gesture::makeGesture(
|
_gestures[d] = Gesture::makeGesture(
|
||||||
_device, type, _config.gestures.value()[dir_name],
|
_device, type, gesture,
|
||||||
_node->make_child(dir_name));
|
_node->make_child(dir_name));
|
||||||
} catch (InvalidGesture& e) {
|
} catch (InvalidGesture& e) {
|
||||||
_gestures[d] = Gesture::makeGesture(
|
_gestures[d] = Gesture::makeGesture(
|
||||||
_device, _config.gestures.value()[dir_name],
|
_device, gesture,
|
||||||
_node->make_child(dir_name));
|
_node->make_child(dir_name));
|
||||||
throw std::invalid_argument("Invalid gesture type");
|
throw std::invalid_argument("Invalid gesture type");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (d == None) {
|
||||||
|
std::visit([](auto&& x) {
|
||||||
|
x.threshold = 0;
|
||||||
|
}, gesture);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -47,5 +47,5 @@ bool NullGesture::wheelCompatibility() const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool NullGesture::metThreshold() const {
|
bool NullGesture::metThreshold() const {
|
||||||
return _axis > _config.threshold.value_or(defaults::gesture_threshold);
|
return _axis >= _config.threshold.value_or(defaults::gesture_threshold);
|
||||||
}
|
}
|
@ -24,21 +24,64 @@
|
|||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <shared_mutex>
|
#include <shared_mutex>
|
||||||
#include <list>
|
#include <list>
|
||||||
|
#include <atomic>
|
||||||
|
|
||||||
template <class T>
|
template <class T>
|
||||||
class EventHandlerLock;
|
class EventHandlerLock;
|
||||||
|
|
||||||
template <class T>
|
template <class T>
|
||||||
struct EventHandlerList {
|
class EventHandlerList {
|
||||||
typedef std::list<typename T::EventHandler> list_t;
|
public:
|
||||||
typedef list_t::const_iterator iterator_t;
|
typedef std::list<std::pair<typename T::EventHandler, std::atomic_bool>> list_t;
|
||||||
|
typedef typename list_t::iterator iterator_t;
|
||||||
|
private:
|
||||||
|
list_t list;
|
||||||
|
std::shared_mutex mutex;
|
||||||
|
std::shared_mutex add_mutex;
|
||||||
|
|
||||||
std::list<typename T::EventHandler> list;
|
void cleanup() {
|
||||||
mutable std::shared_mutex mutex;
|
std::unique_lock lock(mutex, std::try_to_lock);
|
||||||
|
if (lock.owns_lock()) {
|
||||||
|
std::list<iterator_t> 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) {
|
void remove(iterator_t iterator) {
|
||||||
std::unique_lock lock(mutex);
|
std::unique_lock lock(mutex, std::try_to_lock);
|
||||||
list.erase(iterator);
|
if (lock.owns_lock()) {
|
||||||
|
std::unique_lock add_lock(add_mutex);
|
||||||
|
list.erase(iterator);
|
||||||
|
} else {
|
||||||
|
iterator->second = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Arg>
|
||||||
|
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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -53,7 +53,7 @@ Device::Device(const std::string& path, DeviceIndex index,
|
|||||||
const std::shared_ptr<raw::DeviceMonitor>& monitor, double timeout) :
|
const std::shared_ptr<raw::DeviceMonitor>& monitor, double timeout) :
|
||||||
io_timeout(duration_cast<milliseconds>(
|
io_timeout(duration_cast<milliseconds>(
|
||||||
duration<double, std::milli>(timeout))),
|
duration<double, std::milli>(timeout))),
|
||||||
_raw_device(std::make_shared<raw::RawDevice>(path, monitor)),
|
_raw_device(raw::RawDevice::make(path, monitor)),
|
||||||
_receiver(nullptr), _path(path), _index(index) {
|
_receiver(nullptr), _path(path), _index(index) {
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -114,35 +114,8 @@ void Device::_setupReportsAndInit() {
|
|||||||
/* hid_logitech_dj creates virtual /dev/hidraw nodes for receiver
|
/* hid_logitech_dj creates virtual /dev/hidraw nodes for receiver
|
||||||
* devices. We should ignore these devices.
|
* devices. We should ignore these devices.
|
||||||
*/
|
*/
|
||||||
if (_index == hidpp::DefaultDevice) {
|
if (_raw_device->isSubDevice())
|
||||||
_raw_handler = _raw_device->addEventHandler(
|
throw InvalidDevice(InvalidDevice::VirtualNode);
|
||||||
{[](const std::vector<uint8_t>& report) -> bool {
|
|
||||||
return (report[Offset::Type] == Report::Type::Short ||
|
|
||||||
report[Offset::Type] == Report::Type::Long);
|
|
||||||
},
|
|
||||||
[self_weak = _self](const std::vector<uint8_t>& report) -> void {
|
|
||||||
Report _report(report);
|
|
||||||
if(auto self = self_weak.lock())
|
|
||||||
self->handleEvent(_report);
|
|
||||||
}});
|
|
||||||
|
|
||||||
try {
|
|
||||||
auto rsp = sendReport({ReportType::Short, _index,
|
|
||||||
hidpp20::FeatureID::ROOT, hidpp20::Root::Ping,
|
|
||||||
hidpp::softwareID});
|
|
||||||
if (rsp.deviceIndex() != _index)
|
|
||||||
throw InvalidDevice(InvalidDevice::VirtualNode);
|
|
||||||
} catch (hidpp10::Error& e) {
|
|
||||||
if (e.deviceIndex() != _index)
|
|
||||||
throw InvalidDevice(InvalidDevice::VirtualNode);
|
|
||||||
} catch (hidpp20::Error& e) {
|
|
||||||
/* This shouldn't happen, the device must not be ready */
|
|
||||||
if (e.deviceIndex() != _index)
|
|
||||||
throw InvalidDevice(InvalidDevice::VirtualNode);
|
|
||||||
else
|
|
||||||
throw DeviceNotReady();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_raw_handler = _raw_device->addEventHandler(
|
_raw_handler = _raw_device->addEventHandler(
|
||||||
{[index = _index](
|
{[index = _index](
|
||||||
@ -214,25 +187,21 @@ void Device::_init() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
EventHandlerLock<Device> Device::addEventHandler(EventHandler handler) {
|
EventHandlerLock<Device> Device::addEventHandler(EventHandler handler) {
|
||||||
std::unique_lock lock(_event_handlers->mutex);
|
return {_event_handlers, _event_handlers->add(std::move(handler))};
|
||||||
_event_handlers->list.emplace_front(std::move(handler));
|
|
||||||
return {_event_handlers, _event_handlers->list.cbegin()};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Device::handleEvent(Report& report) {
|
void Device::handleEvent(Report& report) {
|
||||||
if (responseReport(report))
|
if (responseReport(report))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
std::shared_lock lock(_event_handlers->mutex);
|
_event_handlers->run_all(report);
|
||||||
for (auto& handler: _event_handlers->list)
|
|
||||||
if (handler.condition(report))
|
|
||||||
handler.callback(report);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Report Device::sendReport(const Report& report) {
|
Report Device::sendReport(const Report& report) {
|
||||||
/* Must complete transaction before next send */
|
/* Must complete transaction before next send */
|
||||||
std::lock_guard send_lock(_send_mutex);
|
std::lock_guard send_lock(_send_mutex);
|
||||||
_sent_sub_id = report.subId();
|
_sent_sub_id = report.subId();
|
||||||
|
_sent_address = report.address();
|
||||||
std::unique_lock lock(_response_mutex);
|
std::unique_lock lock(_response_mutex);
|
||||||
_sendReport(report);
|
_sendReport(report);
|
||||||
|
|
||||||
@ -249,6 +218,7 @@ Report Device::sendReport(const Report& report) {
|
|||||||
Response response = _response.value();
|
Response response = _response.value();
|
||||||
_response.reset();
|
_response.reset();
|
||||||
_sent_sub_id.reset();
|
_sent_sub_id.reset();
|
||||||
|
_sent_address.reset();
|
||||||
|
|
||||||
if (std::holds_alternative<Report>(response)) {
|
if (std::holds_alternative<Report>(response)) {
|
||||||
return std::get<Report>(response);
|
return std::get<Report>(response);
|
||||||
@ -268,20 +238,23 @@ bool Device::responseReport(const Report& report) {
|
|||||||
std::lock_guard lock(_response_mutex);
|
std::lock_guard lock(_response_mutex);
|
||||||
Response response = report;
|
Response response = report;
|
||||||
uint8_t sub_id;
|
uint8_t sub_id;
|
||||||
|
uint8_t address;
|
||||||
|
|
||||||
Report::Hidpp10Error hidpp10_error{};
|
Report::Hidpp10Error hidpp10_error{};
|
||||||
Report::Hidpp20Error hidpp20_error{};
|
Report::Hidpp20Error hidpp20_error{};
|
||||||
if (report.isError10(hidpp10_error)) {
|
if (report.isError10(hidpp10_error)) {
|
||||||
sub_id = hidpp10_error.sub_id;
|
sub_id = hidpp10_error.sub_id;
|
||||||
|
address = hidpp10_error.address;
|
||||||
response = hidpp10_error;
|
response = hidpp10_error;
|
||||||
} else if (report.isError20(hidpp20_error)) {
|
} else if (report.isError20(hidpp20_error)) {
|
||||||
sub_id = hidpp20_error.feature_index;
|
sub_id = hidpp20_error.feature_index;
|
||||||
response = hidpp20_error;
|
address = (hidpp20_error.function << 4) | (hidpp20_error.software_id & 0x0f);
|
||||||
} else {
|
} else {
|
||||||
sub_id = report.subId();
|
sub_id = report.subId();
|
||||||
|
address = report.address();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sub_id == _sent_sub_id) {
|
if (sub_id == _sent_sub_id && address == _sent_address) {
|
||||||
_response = response;
|
_response = response;
|
||||||
_response_cv.notify_all();
|
_response_cv.notify_all();
|
||||||
return true;
|
return true;
|
||||||
@ -310,7 +283,7 @@ bool Device::isStable10() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool Device::isStable20() {
|
bool Device::isStable20() {
|
||||||
static constexpr std::string ping_seq = "hello";
|
static const std::string ping_seq = "hello";
|
||||||
|
|
||||||
hidpp20::Root root(this);
|
hidpp20::Root root(this);
|
||||||
|
|
||||||
|
@ -38,24 +38,25 @@ namespace logid::backend::hidpp10 {
|
|||||||
namespace logid::backend::hidpp {
|
namespace logid::backend::hidpp {
|
||||||
struct DeviceConnectionEvent;
|
struct DeviceConnectionEvent;
|
||||||
|
|
||||||
namespace {
|
template<typename T>
|
||||||
template <typename T>
|
class _deviceWrapper : public T {
|
||||||
class DeviceWrapper : public T {
|
friend class Device;
|
||||||
friend class Device;
|
|
||||||
public:
|
|
||||||
template <typename... Args>
|
|
||||||
explicit DeviceWrapper(Args... args) : T(std::forward<Args>(args)...) { }
|
|
||||||
|
|
||||||
template <typename... Args>
|
public:
|
||||||
static std::shared_ptr<T> make(Args... args) {
|
template<typename... Args>
|
||||||
return std::make_shared<DeviceWrapper>(std::forward<Args>(args)...);
|
explicit _deviceWrapper(Args... args) : T(std::forward<Args>(args)...) {}
|
||||||
}
|
|
||||||
};
|
template<typename... Args>
|
||||||
}
|
static std::shared_ptr<T> make(Args... args) {
|
||||||
|
return std::make_shared<_deviceWrapper>(std::forward<Args>(args)...);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
class Device {
|
class Device {
|
||||||
template <typename T>
|
template<typename T>
|
||||||
friend class DeviceWrapper;
|
friend
|
||||||
|
class _deviceWrapper;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
struct EventHandler {
|
struct EventHandler {
|
||||||
std::function<bool(Report&)> condition;
|
std::function<bool(Report&)> condition;
|
||||||
@ -102,7 +103,9 @@ namespace logid::backend::hidpp {
|
|||||||
[[nodiscard]] const std::shared_ptr<raw::RawDevice>& rawDevice() const;
|
[[nodiscard]] const std::shared_ptr<raw::RawDevice>& rawDevice() const;
|
||||||
|
|
||||||
Device(const Device&) = delete;
|
Device(const Device&) = delete;
|
||||||
|
|
||||||
Device(Device&&) = delete;
|
Device(Device&&) = delete;
|
||||||
|
|
||||||
virtual ~Device() = default;
|
virtual ~Device() = default;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
@ -155,22 +158,23 @@ namespace logid::backend::hidpp {
|
|||||||
|
|
||||||
std::optional<Response> _response;
|
std::optional<Response> _response;
|
||||||
std::optional<uint8_t> _sent_sub_id{};
|
std::optional<uint8_t> _sent_sub_id{};
|
||||||
|
std::optional<uint8_t> _sent_address{};
|
||||||
|
|
||||||
std::shared_ptr<EventHandlerList<Device>> _event_handlers;
|
std::shared_ptr<EventHandlerList<Device>> _event_handlers;
|
||||||
|
|
||||||
std::weak_ptr<Device> _self;
|
std::weak_ptr<Device> _self;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
template <typename T, typename... Args>
|
template<typename T, typename... Args>
|
||||||
static std::shared_ptr<T> makeDerived(Args... args) {
|
static std::shared_ptr<T> makeDerived(Args... args) {
|
||||||
auto device = DeviceWrapper<T>::make(std::forward<Args>(args)...);
|
auto device = _deviceWrapper<T>::make(std::forward<Args>(args)...);
|
||||||
device->_self = device;
|
device->_self = device;
|
||||||
device->_setupReportsAndInit();
|
device->_setupReportsAndInit();
|
||||||
return device;
|
return device;
|
||||||
}
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
template <typename... Args>
|
template<typename... Args>
|
||||||
static std::shared_ptr<Device> make(Args... args) {
|
static std::shared_ptr<Device> make(Args... args) {
|
||||||
return makeDerived<Device>(std::forward<Args>(args)...);
|
return makeDerived<Device>(std::forward<Args>(args)...);
|
||||||
}
|
}
|
||||||
|
@ -42,7 +42,7 @@ namespace logid::backend::hidpp {
|
|||||||
|
|
||||||
static constexpr uint8_t softwareID = 2;
|
static constexpr uint8_t softwareID = 2;
|
||||||
/* For sending reports with no response, use a different SW ID */
|
/* For sending reports with no response, use a different SW ID */
|
||||||
static constexpr uint8_t noAckSoftwareID = 1;
|
static constexpr uint8_t noAckSoftwareID = 3;
|
||||||
|
|
||||||
static constexpr std::size_t ShortParamLength = 3;
|
static constexpr std::size_t ShortParamLength = 3;
|
||||||
static constexpr std::size_t LongParamLength = 16;
|
static constexpr std::size_t LongParamLength = 16;
|
||||||
|
@ -24,6 +24,23 @@
|
|||||||
using namespace logid::backend;
|
using namespace logid::backend;
|
||||||
using namespace logid::backend::hidpp10;
|
using namespace logid::backend::hidpp10;
|
||||||
|
|
||||||
|
hidpp::Report setupRegReport(hidpp::DeviceIndex index,
|
||||||
|
uint8_t sub_id, uint8_t address,
|
||||||
|
const std::vector<uint8_t>& 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,
|
Device::Device(const std::string& path, hidpp::DeviceIndex index,
|
||||||
const std::shared_ptr<raw::DeviceMonitor>& monitor, double timeout) :
|
const std::shared_ptr<raw::DeviceMonitor>& monitor, double timeout) :
|
||||||
hidpp::Device(path, index, monitor, timeout) {
|
hidpp::Device(path, index, monitor, timeout) {
|
||||||
@ -124,22 +141,24 @@ std::vector<uint8_t> Device::setRegister(uint8_t address,
|
|||||||
return accessRegister(sub_id, address, params);
|
return accessRegister(sub_id, address, params);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Device::setRegisterNoResponse(uint8_t address,
|
||||||
|
const std::vector<uint8_t>& 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<uint8_t> Device::accessRegister(uint8_t sub_id, uint8_t address,
|
std::vector<uint8_t> Device::accessRegister(uint8_t sub_id, uint8_t address,
|
||||||
const std::vector<uint8_t>& params) {
|
const std::vector<uint8_t>& params) {
|
||||||
hidpp::Report::Type type = params.size() <= hidpp::ShortParamLength ?
|
auto response = sendReport(setupRegReport(deviceIndex(), sub_id, address, params));
|
||||||
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, deviceIndex(), sub_id, address);
|
|
||||||
std::copy(params.begin(), params.end(), request.paramBegin());
|
|
||||||
|
|
||||||
auto response = sendReport(request);
|
|
||||||
return {response.paramBegin(), response.paramEnd()};
|
return {response.paramBegin(), response.paramEnd()};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Device::accessRegisterNoResponse(uint8_t sub_id, uint8_t address,
|
||||||
|
const std::vector<uint8_t>& params) {
|
||||||
|
sendReportNoACK(setupRegReport(deviceIndex(), sub_id, address, params));
|
||||||
|
}
|
||||||
|
@ -39,6 +39,9 @@ namespace logid::backend::hidpp10 {
|
|||||||
const std::vector<uint8_t>& params,
|
const std::vector<uint8_t>& params,
|
||||||
hidpp::Report::Type type);
|
hidpp::Report::Type type);
|
||||||
|
|
||||||
|
void setRegisterNoResponse(uint8_t address, const std::vector<uint8_t>& params,
|
||||||
|
hidpp::Report::Type type);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
Device(const std::string& path, hidpp::DeviceIndex index,
|
Device(const std::string& path, hidpp::DeviceIndex index,
|
||||||
const std::shared_ptr<raw::DeviceMonitor>& monitor, double timeout);
|
const std::shared_ptr<raw::DeviceMonitor>& monitor, double timeout);
|
||||||
@ -53,18 +56,24 @@ namespace logid::backend::hidpp10 {
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
typedef std::variant<hidpp::Report, hidpp::Report::Hidpp10Error> Response;
|
typedef std::variant<hidpp::Report, hidpp::Report::Hidpp10Error> Response;
|
||||||
|
|
||||||
struct ResponseSlot {
|
struct ResponseSlot {
|
||||||
std::optional<Response> response;
|
std::optional<Response> response;
|
||||||
std::optional<uint8_t> sub_id;
|
std::optional<uint8_t> sub_id;
|
||||||
|
|
||||||
void reset();
|
void reset();
|
||||||
};
|
};
|
||||||
|
|
||||||
std::array<ResponseSlot, SubIDCount> _responses;
|
std::array<ResponseSlot, SubIDCount> _responses;
|
||||||
|
|
||||||
std::vector<uint8_t> accessRegister(
|
std::vector<uint8_t> accessRegister(
|
||||||
uint8_t sub_id, uint8_t address, const std::vector<uint8_t>& params);
|
uint8_t sub_id, uint8_t address, const std::vector<uint8_t>& params);
|
||||||
|
|
||||||
|
void accessRegisterNoResponse(
|
||||||
|
uint8_t sub_id, uint8_t address, const std::vector<uint8_t>& params);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
template <typename T, typename... Args>
|
template<typename T, typename... Args>
|
||||||
static std::shared_ptr<T> makeDerived(Args... args) {
|
static std::shared_ptr<T> makeDerived(Args... args) {
|
||||||
auto device = hidpp::Device::makeDerived<T>(std::forward<Args>(args)...);
|
auto device = hidpp::Device::makeDerived<T>(std::forward<Args>(args)...);
|
||||||
|
|
||||||
@ -73,8 +82,9 @@ namespace logid::backend::hidpp10 {
|
|||||||
|
|
||||||
return device;
|
return device;
|
||||||
}
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
template <typename... Args>
|
template<typename... Args>
|
||||||
static std::shared_ptr<Device> make(Args... args) {
|
static std::shared_ptr<Device> make(Args... args) {
|
||||||
return makeDerived<Device>(std::forward<Args>(args)...);
|
return makeDerived<Device>(std::forward<Args>(args)...);
|
||||||
}
|
}
|
||||||
|
@ -68,7 +68,7 @@ void Receiver::setNotifications(NotificationFlags flags) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Receiver::enumerate() {
|
void Receiver::enumerate() {
|
||||||
setRegister(ConnectionState, {2}, hidpp::ReportType::Short);
|
setRegisterNoResponse(ConnectionState, {2}, hidpp::ReportType::Short);
|
||||||
}
|
}
|
||||||
|
|
||||||
///TODO: Investigate usage
|
///TODO: Investigate usage
|
||||||
|
@ -158,28 +158,31 @@ void ReceiverMonitor::enumerate() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void ReceiverMonitor::waitForDevice(hidpp::DeviceIndex index) {
|
void ReceiverMonitor::waitForDevice(hidpp::DeviceIndex index) {
|
||||||
auto handler_id = std::make_shared<EventHandlerLock<raw::RawDevice>>();
|
const std::lock_guard lock(_wait_mutex);
|
||||||
|
if (!_waiters.count(index)) {
|
||||||
|
_waiters.emplace(index, _receiver->rawDevice()->addEventHandler(
|
||||||
|
{[index](const std::vector<uint8_t>& 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<uint8_t>& report) {
|
||||||
|
hidpp::DeviceConnectionEvent event{};
|
||||||
|
event.withPayload = false;
|
||||||
|
event.linkEstablished = true;
|
||||||
|
event.index = index;
|
||||||
|
event.fromTimeoutCheck = true;
|
||||||
|
|
||||||
*handler_id = _receiver->rawDevice()->addEventHandler(
|
run_task([self_weak, event]() {
|
||||||
{[index](const std::vector<uint8_t>& report) -> bool {
|
if (auto self = self_weak.lock())
|
||||||
return report[Offset::DeviceIndex] == index;
|
self->_addHandler(event);
|
||||||
},
|
});
|
||||||
[self_weak = _self, index, handler_id](
|
}
|
||||||
[[maybe_unused]] const std::vector<uint8_t>& report) {
|
}));
|
||||||
hidpp::DeviceConnectionEvent event{};
|
}
|
||||||
event.withPayload = false;
|
|
||||||
event.linkEstablished = true;
|
|
||||||
event.index = index;
|
|
||||||
event.fromTimeoutCheck = true;
|
|
||||||
|
|
||||||
run_task([self_weak, event, handler_id]() {
|
|
||||||
*handler_id = {};
|
|
||||||
if (auto self = self_weak.lock()) {
|
|
||||||
self->_addHandler(event);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<Receiver> ReceiverMonitor::receiver() const {
|
std::shared_ptr<Receiver> ReceiverMonitor::receiver() const {
|
||||||
@ -217,6 +220,8 @@ void ReceiverMonitor::_addHandler(const hidpp::DeviceConnectionEvent& event, int
|
|||||||
auto device_path = _receiver->devicePath();
|
auto device_path = _receiver->devicePath();
|
||||||
try {
|
try {
|
||||||
addDevice(event);
|
addDevice(event);
|
||||||
|
const std::lock_guard lock(_wait_mutex);
|
||||||
|
_waiters.erase(event.index);
|
||||||
} catch (DeviceNotReady& e) {
|
} catch (DeviceNotReady& e) {
|
||||||
if (tries == max_tries) {
|
if (tries == max_tries) {
|
||||||
logPrintf(WARN, "Failed to add device %s:%d after %d tries."
|
logPrintf(WARN, "Failed to add device %s:%d after %d tries."
|
||||||
|
@ -26,20 +26,19 @@
|
|||||||
|
|
||||||
namespace logid::backend::hidpp10 {
|
namespace logid::backend::hidpp10 {
|
||||||
|
|
||||||
namespace {
|
template<typename T>
|
||||||
template <typename T>
|
class _receiverMonitorWrapper : public T {
|
||||||
class ReceiverMonitorWrapper : public T {
|
friend class ReceiverMonitor;
|
||||||
friend class ReceiverMonitor;
|
|
||||||
public:
|
|
||||||
template <typename... Args>
|
|
||||||
explicit ReceiverMonitorWrapper(Args... args) : T(std::forward<Args>(args)...) { }
|
|
||||||
|
|
||||||
template <typename... Args>
|
public:
|
||||||
static std::shared_ptr<T> make(Args... args) {
|
template<typename... Args>
|
||||||
return std::make_shared<ReceiverMonitorWrapper>(std::forward<Args>(args)...);
|
explicit _receiverMonitorWrapper(Args... args) : T(std::forward<Args>(args)...) {}
|
||||||
}
|
|
||||||
};
|
template<typename... Args>
|
||||||
}
|
static std::shared_ptr<T> make(Args... args) {
|
||||||
|
return std::make_shared<_receiverMonitorWrapper>(std::forward<Args>(args)...);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
static constexpr int max_tries = 5;
|
static constexpr int max_tries = 5;
|
||||||
static constexpr int ready_backoff = 250;
|
static constexpr int ready_backoff = 250;
|
||||||
@ -50,7 +49,9 @@ namespace logid::backend::hidpp10 {
|
|||||||
void enumerate();
|
void enumerate();
|
||||||
|
|
||||||
ReceiverMonitor(const ReceiverMonitor&) = delete;
|
ReceiverMonitor(const ReceiverMonitor&) = delete;
|
||||||
|
|
||||||
ReceiverMonitor(ReceiverMonitor&&) = delete;
|
ReceiverMonitor(ReceiverMonitor&&) = delete;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
ReceiverMonitor(const std::string& path,
|
ReceiverMonitor(const std::string& path,
|
||||||
const std::shared_ptr<raw::DeviceMonitor>& monitor,
|
const std::shared_ptr<raw::DeviceMonitor>& monitor,
|
||||||
@ -100,10 +101,13 @@ namespace logid::backend::hidpp10 {
|
|||||||
|
|
||||||
std::weak_ptr<ReceiverMonitor> _self;
|
std::weak_ptr<ReceiverMonitor> _self;
|
||||||
|
|
||||||
|
std::mutex _wait_mutex;
|
||||||
|
std::map<hidpp::DeviceIndex, EventHandlerLock<raw::RawDevice>> _waiters;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
template <typename T, typename... Args>
|
template<typename T, typename... Args>
|
||||||
static std::shared_ptr<T> make(Args... args) {
|
static std::shared_ptr<T> make(Args... args) {
|
||||||
auto receiver_monitor = ReceiverMonitorWrapper<T>::make(std::forward<Args>(args)...);
|
auto receiver_monitor = _receiverMonitorWrapper<T>::make(std::forward<Args>(args)...);
|
||||||
receiver_monitor->_self = receiver_monitor;
|
receiver_monitor->_self = receiver_monitor;
|
||||||
receiver_monitor->_ready();
|
receiver_monitor->_ready();
|
||||||
return receiver_monitor;
|
return receiver_monitor;
|
||||||
|
@ -74,6 +74,7 @@ namespace logid::backend::hidpp20 {
|
|||||||
POINTER_AXES_ORIENTATION = 0x2006,
|
POINTER_AXES_ORIENTATION = 0x2006,
|
||||||
VERTICAL_SCROLLING = 0x2100,
|
VERTICAL_SCROLLING = 0x2100,
|
||||||
SMART_SHIFT = 0x2110,
|
SMART_SHIFT = 0x2110,
|
||||||
|
SMART_SHIFT_V2 = 0x2111,
|
||||||
HIRES_SCROLLING = 0x2120,
|
HIRES_SCROLLING = 0x2120,
|
||||||
HIRES_SCROLLING_V2 = 0x2121, // Referred to as Hi-res wheel in cvuchener/hidpp, seems to be V2?
|
HIRES_SCROLLING_V2 = 0x2121, // Referred to as Hi-res wheel in cvuchener/hidpp, seems to be V2?
|
||||||
LORES_SCROLLING = 0x2130,
|
LORES_SCROLLING = 0x2130,
|
||||||
|
@ -40,7 +40,7 @@ namespace logid::backend::hidpp20 {
|
|||||||
struct HostInfo {
|
struct HostInfo {
|
||||||
uint8_t hostCount;
|
uint8_t hostCount;
|
||||||
uint8_t currentHost;
|
uint8_t currentHost;
|
||||||
[[maybe_unused]] bool enhancedHostSwitch;
|
bool enhancedHostSwitch;
|
||||||
};
|
};
|
||||||
|
|
||||||
HostInfo getHostInfo();
|
HostInfo getHostInfo();
|
||||||
|
@ -129,7 +129,7 @@ ReprogControls::ControlInfo ReprogControls::getControlIdInfo(uint16_t cid) {
|
|||||||
return report;
|
return report;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ReprogControls::setControlReporting(uint8_t cid, ControlInfo info) {
|
void ReprogControls::setControlReporting(uint16_t cid, ControlInfo info) {
|
||||||
// This function does not exist pre-v4 and cannot be emulated, ignore.
|
// This function does not exist pre-v4 and cannot be emulated, ignore.
|
||||||
(void) cid;
|
(void) cid;
|
||||||
(void) info; // Suppress unused warnings
|
(void) info; // Suppress unused warnings
|
||||||
@ -173,7 +173,7 @@ ReprogControls::ControlInfo ReprogControlsV4::getControlReporting(uint16_t cid)
|
|||||||
return info;
|
return info;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ReprogControlsV4::setControlReporting(uint8_t cid, ControlInfo info) {
|
void ReprogControlsV4::setControlReporting(uint16_t cid, ControlInfo info) {
|
||||||
std::vector<uint8_t> params(5);
|
std::vector<uint8_t> params(5);
|
||||||
params[0] = (cid >> 8) & 0xff;
|
params[0] = (cid >> 8) & 0xff;
|
||||||
params[1] = cid & 0xff;
|
params[1] = cid & 0xff;
|
||||||
|
@ -100,7 +100,7 @@ namespace logid::backend::hidpp20 {
|
|||||||
[[nodiscard]] virtual ControlInfo getControlReporting(uint16_t cid);
|
[[nodiscard]] virtual ControlInfo getControlReporting(uint16_t cid);
|
||||||
|
|
||||||
// Only controlId (for remap) and flags will be read
|
// Only controlId (for remap) and flags will be read
|
||||||
virtual void setControlReporting(uint8_t cid, ControlInfo info);
|
virtual void setControlReporting(uint16_t cid, ControlInfo info);
|
||||||
|
|
||||||
[[nodiscard]] static std::set<uint16_t> divertedButtonEvent(const hidpp::Report& report);
|
[[nodiscard]] static std::set<uint16_t> divertedButtonEvent(const hidpp::Report& report);
|
||||||
|
|
||||||
@ -162,7 +162,7 @@ namespace logid::backend::hidpp20 {
|
|||||||
|
|
||||||
[[nodiscard]] ControlInfo getControlReporting(uint16_t cid) override;
|
[[nodiscard]] ControlInfo getControlReporting(uint16_t cid) override;
|
||||||
|
|
||||||
void setControlReporting(uint8_t cid, ControlInfo info) override;
|
void setControlReporting(uint16_t cid, ControlInfo info) override;
|
||||||
|
|
||||||
explicit ReprogControlsV4(Device* dev);
|
explicit ReprogControlsV4(Device* dev);
|
||||||
|
|
||||||
|
@ -19,26 +19,107 @@
|
|||||||
|
|
||||||
using namespace logid::backend::hidpp20;
|
using namespace logid::backend::hidpp20;
|
||||||
|
|
||||||
SmartShift::SmartShift(Device* dev) : Feature(dev, ID) {
|
SmartShift::SmartShift(Device* dev) : SmartShift(dev, ID) {
|
||||||
}
|
}
|
||||||
|
|
||||||
SmartShift::SmartshiftStatus SmartShift::getStatus() {
|
SmartShift::SmartShift(Device* dev, uint16_t feature_id) :
|
||||||
|
Feature(dev, feature_id) {
|
||||||
|
}
|
||||||
|
|
||||||
|
SmartShiftV2::SmartShiftV2(Device* dev) : SmartShift(dev, ID) {
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
std::shared_ptr<T> make_smartshift(Device* dev) {
|
||||||
|
try {
|
||||||
|
return std::make_shared<T>(dev);
|
||||||
|
} catch (UnsupportedFeature& e) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<SmartShift> SmartShift::autoVersion(Device* dev) {
|
||||||
|
if (auto v2 = make_smartshift<SmartShiftV2>(dev))
|
||||||
|
return v2;
|
||||||
|
|
||||||
|
return std::make_shared<SmartShift>(dev);
|
||||||
|
}
|
||||||
|
|
||||||
|
SmartShift::Status SmartShift::getStatus() {
|
||||||
std::vector<uint8_t> params(0);
|
std::vector<uint8_t> params(0);
|
||||||
SmartshiftStatus status{};
|
|
||||||
auto response = callFunction(GetStatus, params);
|
auto response = callFunction(GetStatus, params);
|
||||||
status.active = response[0] - 1;
|
|
||||||
status.autoDisengage = response[1];
|
return {
|
||||||
status.defaultAutoDisengage = response[2];
|
.active = static_cast<bool>(response[0] - 1),
|
||||||
return status;
|
.autoDisengage = response[1],
|
||||||
|
.torque = 0,
|
||||||
|
.setActive = false,
|
||||||
|
.setAutoDisengage = false,
|
||||||
|
.setTorque = false
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
void SmartShift::setStatus(SmartshiftStatus status) {
|
SmartShift::Defaults SmartShift::getDefaults() {
|
||||||
|
std::vector<uint8_t> params(0);
|
||||||
|
|
||||||
|
auto response = callFunction(GetStatus, params);
|
||||||
|
|
||||||
|
return {
|
||||||
|
.autoDisengage = response[2],
|
||||||
|
.torque = 0,
|
||||||
|
.maxForce = 0,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
void SmartShift::setStatus(Status status) {
|
||||||
std::vector<uint8_t> params(3);
|
std::vector<uint8_t> params(3);
|
||||||
if (status.setActive)
|
if (status.setActive)
|
||||||
params[0] = status.active + 1;
|
params[0] = status.active + 1;
|
||||||
if (status.setAutoDisengage)
|
if (status.setAutoDisengage)
|
||||||
params[1] = status.autoDisengage;
|
params[1] = status.autoDisengage;
|
||||||
if (status.setDefaultAutoDisengage)
|
|
||||||
params[2] = status.defaultAutoDisengage;
|
|
||||||
callFunction(SetStatus, params);
|
callFunction(SetStatus, params);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SmartShift::Defaults SmartShiftV2::getDefaults() {
|
||||||
|
std::vector<uint8_t> params(0);
|
||||||
|
auto response = callFunction(GetCapabilities, params);
|
||||||
|
|
||||||
|
return {
|
||||||
|
.autoDisengage = response[1],
|
||||||
|
.torque = response[2],
|
||||||
|
.maxForce = response[3],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
SmartShift::Status SmartShiftV2::getStatus() {
|
||||||
|
std::vector<uint8_t> params(0);
|
||||||
|
auto response = callFunction(GetStatus, params);
|
||||||
|
|
||||||
|
return {
|
||||||
|
.active = static_cast<bool>(response[0] - 1),
|
||||||
|
.autoDisengage = response[1],
|
||||||
|
.torque = response[2],
|
||||||
|
.setActive = false, .setAutoDisengage = false, .setTorque = false,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
void SmartShiftV2::setStatus(Status status) {
|
||||||
|
std::vector<uint8_t> 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<uint8_t> params(0);
|
||||||
|
auto response = callFunction(GetCapabilities, params);
|
||||||
|
|
||||||
|
return static_cast<bool>(response[0] & 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@ -20,13 +20,14 @@
|
|||||||
|
|
||||||
#include <backend/hidpp20/feature_defs.h>
|
#include <backend/hidpp20/feature_defs.h>
|
||||||
#include <backend/hidpp20/Feature.h>
|
#include <backend/hidpp20/Feature.h>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
namespace logid::backend::hidpp20 {
|
namespace logid::backend::hidpp20 {
|
||||||
class SmartShift : public Feature {
|
class SmartShift : public Feature {
|
||||||
public:
|
public:
|
||||||
static const uint16_t ID = FeatureID::SMART_SHIFT;
|
static const uint16_t ID = FeatureID::SMART_SHIFT;
|
||||||
|
|
||||||
uint16_t getID() final { return ID; }
|
uint16_t getID() override { return ID; }
|
||||||
|
|
||||||
enum Function {
|
enum Function {
|
||||||
GetStatus = 0,
|
GetStatus = 0,
|
||||||
@ -35,16 +36,54 @@ namespace logid::backend::hidpp20 {
|
|||||||
|
|
||||||
explicit SmartShift(Device* dev);
|
explicit SmartShift(Device* dev);
|
||||||
|
|
||||||
struct SmartshiftStatus {
|
struct Defaults {
|
||||||
bool active;
|
|
||||||
uint8_t autoDisengage;
|
uint8_t autoDisengage;
|
||||||
uint8_t defaultAutoDisengage;
|
uint8_t torque;
|
||||||
bool setActive, setAutoDisengage, setDefaultAutoDisengage;
|
uint8_t maxForce;
|
||||||
};
|
};
|
||||||
|
|
||||||
SmartshiftStatus getStatus();
|
struct Status {
|
||||||
|
bool active;
|
||||||
|
uint8_t autoDisengage;
|
||||||
|
uint8_t torque;
|
||||||
|
bool setActive, setAutoDisengage, setTorque;
|
||||||
|
};
|
||||||
|
|
||||||
void setStatus(SmartshiftStatus status);
|
[[nodiscard]] virtual bool supportsTorque() { return false; }
|
||||||
|
|
||||||
|
[[nodiscard]] virtual Defaults getDefaults();
|
||||||
|
|
||||||
|
[[nodiscard]] virtual Status getStatus();
|
||||||
|
|
||||||
|
virtual void setStatus(Status status);
|
||||||
|
|
||||||
|
[[nodiscard]] static std::shared_ptr<SmartShift> 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;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -89,23 +89,25 @@ void DeviceMonitor::ready() {
|
|||||||
_ready = true;
|
_ready = true;
|
||||||
|
|
||||||
_io_monitor->add(_fd, {
|
_io_monitor->add(_fd, {
|
||||||
[this]() {
|
[self_weak = _self]() {
|
||||||
struct udev_device* device = udev_monitor_receive_device(_udev_monitor);
|
if (auto self = self_weak.lock()) {
|
||||||
std::string action = udev_device_get_action(device);
|
struct udev_device* device = udev_monitor_receive_device(self->_udev_monitor);
|
||||||
std::string dev_node = udev_device_get_devnode(device);
|
std::string action = udev_device_get_action(device);
|
||||||
|
std::string dev_node = udev_device_get_devnode(device);
|
||||||
|
|
||||||
if (action == "add")
|
if (action == "add")
|
||||||
run_task([self_weak = _self, dev_node]() {
|
run_task([self_weak, dev_node]() {
|
||||||
if (auto self = self_weak.lock())
|
if (auto self = self_weak.lock())
|
||||||
self->_addHandler(dev_node);
|
self->_addHandler(dev_node);
|
||||||
});
|
});
|
||||||
else if (action == "remove")
|
else if (action == "remove")
|
||||||
run_task([self_weak = _self, dev_node]() {
|
run_task([self_weak, dev_node]() {
|
||||||
if (auto self = self_weak.lock())
|
if (auto self = self_weak.lock())
|
||||||
self->_removeHandler(dev_node);
|
self->_removeHandler(dev_node);
|
||||||
});
|
});
|
||||||
|
|
||||||
udev_device_unref(device);
|
udev_device_unref(device);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
[]() {
|
[]() {
|
||||||
throw std::runtime_error("udev hangup");
|
throw std::runtime_error("udev hangup");
|
||||||
@ -135,13 +137,17 @@ void DeviceMonitor::enumerate() {
|
|||||||
const char* name = udev_list_entry_get_name(udev_enum_entry);
|
const char* name = udev_list_entry_get_name(udev_enum_entry);
|
||||||
|
|
||||||
struct udev_device* device = udev_device_new_from_syspath(_udev_context, name);
|
struct udev_device* device = udev_device_new_from_syspath(_udev_context, name);
|
||||||
if (!device)
|
if (device) {
|
||||||
throw std::runtime_error("udev_device_new_from_syspath failed");
|
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);
|
||||||
|
|
||||||
std::string dev_node = udev_device_get_devnode(device);
|
_addHandler(dev_node);
|
||||||
udev_device_unref(device);
|
} else {
|
||||||
|
udev_device_unref(device);
|
||||||
_addHandler(dev_node);
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
udev_enumerate_unref(udev_enum);
|
udev_enumerate_unref(udev_enum);
|
||||||
|
@ -36,21 +36,19 @@ namespace logid::backend::raw {
|
|||||||
static constexpr int max_tries = 5;
|
static constexpr int max_tries = 5;
|
||||||
static constexpr int ready_backoff = 500;
|
static constexpr int ready_backoff = 500;
|
||||||
|
|
||||||
namespace {
|
template<typename T>
|
||||||
template<typename T>
|
class _deviceMonitorWrapper : public T {
|
||||||
class DeviceMonitorWrapper : public T {
|
friend class Device;
|
||||||
friend class Device;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
template<typename... Args>
|
template<typename... Args>
|
||||||
explicit DeviceMonitorWrapper(Args... args) : T(std::forward<Args>(args)...) {}
|
explicit _deviceMonitorWrapper(Args... args) : T(std::forward<Args>(args)...) {}
|
||||||
|
|
||||||
template<typename... Args>
|
template<typename... Args>
|
||||||
static std::shared_ptr<T> make(Args... args) {
|
static std::shared_ptr<T> make(Args... args) {
|
||||||
return std::make_shared<DeviceMonitorWrapper>(std::forward<Args>(args)...);
|
return std::make_shared<_deviceMonitorWrapper>(std::forward<Args>(args)...);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
|
||||||
|
|
||||||
class DeviceMonitor {
|
class DeviceMonitor {
|
||||||
public:
|
public:
|
||||||
@ -62,7 +60,7 @@ namespace logid::backend::raw {
|
|||||||
|
|
||||||
template<typename T, typename... Args>
|
template<typename T, typename... Args>
|
||||||
static std::shared_ptr<T> make(Args... args) {
|
static std::shared_ptr<T> make(Args... args) {
|
||||||
auto device_monitor = DeviceMonitorWrapper<T>::make(std::forward<Args>(args)...);
|
auto device_monitor = _deviceMonitorWrapper<T>::make(std::forward<Args>(args)...);
|
||||||
device_monitor->_self = device_monitor;
|
device_monitor->_self = device_monitor;
|
||||||
device_monitor->ready();
|
device_monitor->ready();
|
||||||
|
|
||||||
@ -79,7 +77,7 @@ namespace logid::backend::raw {
|
|||||||
|
|
||||||
virtual void removeDevice(std::string device) = 0;
|
virtual void removeDevice(std::string device) = 0;
|
||||||
|
|
||||||
template <typename T>
|
template<typename T>
|
||||||
[[nodiscard]] std::weak_ptr<T> self() const {
|
[[nodiscard]] std::weak_ptr<T> self() const {
|
||||||
return std::dynamic_pointer_cast<T>(_self.lock());
|
return std::dynamic_pointer_cast<T>(_self.lock());
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2022 PixlOne
|
* Copyright 2019-2023 PixlOne
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
@ -16,7 +16,8 @@
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
#include <backend/raw/IOMonitor.h>
|
#include <backend/raw/IOMonitor.h>
|
||||||
#include <cassert>
|
#include <util/log.h>
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
extern "C"
|
extern "C"
|
||||||
{
|
{
|
||||||
@ -56,12 +57,7 @@ IOMonitor::IOMonitor() : _epoll_fd(epoll_create1(0)),
|
|||||||
throw std::system_error(errno, std::generic_category());
|
throw std::system_error(errno, std::generic_category());
|
||||||
}
|
}
|
||||||
|
|
||||||
_fds.emplace(std::piecewise_construct, std::forward_as_tuple(_event_fd),
|
_fds.emplace(_event_fd, nullptr);
|
||||||
std::forward_as_tuple([]() {}, []() {
|
|
||||||
throw std::runtime_error("event_fd hangup");
|
|
||||||
}, []() {
|
|
||||||
throw std::runtime_error("event_fd error");
|
|
||||||
}));
|
|
||||||
|
|
||||||
_io_thread = std::make_unique<std::thread>([this]() {
|
_io_thread = std::make_unique<std::thread>([this]() {
|
||||||
_listen();
|
_listen();
|
||||||
@ -69,120 +65,103 @@ IOMonitor::IOMonitor() : _epoll_fd(epoll_create1(0)),
|
|||||||
}
|
}
|
||||||
|
|
||||||
IOMonitor::~IOMonitor() noexcept {
|
IOMonitor::~IOMonitor() noexcept {
|
||||||
std::lock_guard<std::mutex> ctl_lock(_ctl_lock);
|
|
||||||
_stop();
|
_stop();
|
||||||
|
|
||||||
if (_event_fd >= 0)
|
if (_event_fd >= 0)
|
||||||
close(_event_fd);
|
::close(_event_fd);
|
||||||
|
|
||||||
if (_epoll_fd >= 0)
|
if (_epoll_fd >= 0)
|
||||||
close(_epoll_fd);
|
::close(_epoll_fd);
|
||||||
}
|
}
|
||||||
|
|
||||||
void IOMonitor::_listen() {
|
void IOMonitor::_listen() {
|
||||||
std::lock_guard<std::mutex> run_lock(_run_lock);
|
std::unique_lock lock(_run_mutex);
|
||||||
std::vector<struct epoll_event> events;
|
std::vector<struct epoll_event> events;
|
||||||
|
|
||||||
|
if (_is_running)
|
||||||
|
throw std::runtime_error("IOMonitor double run");
|
||||||
|
|
||||||
_is_running = true;
|
_is_running = true;
|
||||||
|
|
||||||
while (_is_running) {
|
while (_is_running) {
|
||||||
if (_interrupting) {
|
|
||||||
std::unique_lock<std::mutex> lock(_interrupt_mutex);
|
|
||||||
_interrupt_cv.wait(lock, [this]() {
|
|
||||||
return !(bool) _interrupting;
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!_is_running)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::lock_guard<std::mutex> io_lock(_io_lock);
|
|
||||||
if (events.size() != _fds.size())
|
if (events.size() != _fds.size())
|
||||||
events.resize(_fds.size());
|
events.resize(_fds.size());
|
||||||
|
|
||||||
int ev_count = ::epoll_wait(_epoll_fd, events.data(), (int) events.size(), -1);
|
int ev_count = ::epoll_wait(_epoll_fd, events.data(), (int) events.size(), -1);
|
||||||
for (int i = 0; i < ev_count; ++i) {
|
for (int i = 0; i < ev_count; ++i) {
|
||||||
const auto& handler = _fds.at(events[i].data.fd);
|
std::shared_ptr<IOHandler> handler;
|
||||||
if (events[i].events & EPOLLIN)
|
|
||||||
handler.read();
|
if (events[i].data.fd == _event_fd) {
|
||||||
if (events[i].events & EPOLLHUP)
|
if (events[i].events & EPOLLIN) {
|
||||||
handler.hangup();
|
lock.unlock();
|
||||||
if (events[i].events & EPOLLERR)
|
/* Wait until done yielding */
|
||||||
handler.error();
|
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 {
|
void IOMonitor::_stop() noexcept {
|
||||||
_interrupt();
|
|
||||||
_is_running = false;
|
_is_running = false;
|
||||||
_continue();
|
_yield();
|
||||||
_io_thread->join();
|
_io_thread->join();
|
||||||
}
|
}
|
||||||
|
|
||||||
[[maybe_unused]]
|
std::unique_lock<std::mutex> IOMonitor::_yield() noexcept {
|
||||||
bool IOMonitor::_running() const {
|
/* Prevent listener thread from grabbing lock during yielding */
|
||||||
std::unique_lock<std::mutex> run_lock(_run_lock, std::try_to_lock);
|
std::unique_lock yield_lock(_yield_mutex);
|
||||||
return !run_lock.owns_lock() || _is_running;
|
|
||||||
|
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<std::mutex>(_run_mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
return run_lock;
|
||||||
}
|
}
|
||||||
|
|
||||||
void IOMonitor::add(int fd, IOHandler handler) {
|
void IOMonitor::add(int fd, IOHandler handler) {
|
||||||
std::lock_guard<std::mutex> lock(_ctl_lock);
|
const auto lock = _yield();
|
||||||
_interrupt();
|
|
||||||
|
|
||||||
struct epoll_event event{};
|
struct epoll_event event{};
|
||||||
event.events = EPOLLIN | EPOLLHUP | EPOLLERR;
|
event.events = EPOLLIN | EPOLLHUP | EPOLLERR;
|
||||||
event.data.fd = fd;
|
event.data.fd = fd;
|
||||||
|
|
||||||
// TODO: EPOLL_CTL_MOD
|
if (!_fds.contains(fd)) {
|
||||||
if (_fds.contains(fd)) {
|
if (::epoll_ctl(_epoll_fd, EPOLL_CTL_ADD, fd, &event))
|
||||||
_continue();
|
throw std::system_error(errno, std::generic_category());
|
||||||
|
_fds.emplace(fd, std::make_shared<IOHandler>(std::move(handler)));
|
||||||
|
} else {
|
||||||
throw std::runtime_error("duplicate io fd");
|
throw std::runtime_error("duplicate io fd");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (::epoll_ctl(_epoll_fd, EPOLL_CTL_ADD, fd, &event)) {
|
|
||||||
_continue();
|
|
||||||
throw std::system_error(errno, std::generic_category());
|
|
||||||
}
|
|
||||||
_fds.emplace(fd, std::move(handler));
|
|
||||||
|
|
||||||
_continue();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void IOMonitor::remove(int fd) noexcept {
|
void IOMonitor::remove(int fd) noexcept {
|
||||||
std::lock_guard<std::mutex> lock(_ctl_lock);
|
const auto lock = _yield();
|
||||||
_interrupt();
|
|
||||||
std::lock_guard<std::mutex> io_lock(_io_lock);
|
|
||||||
|
|
||||||
::epoll_ctl(_epoll_fd, EPOLL_CTL_DEL, fd, nullptr);
|
::epoll_ctl(_epoll_fd, EPOLL_CTL_DEL, fd, nullptr);
|
||||||
_fds.erase(fd);
|
_fds.erase(fd);
|
||||||
|
}
|
||||||
_continue();
|
|
||||||
}
|
|
||||||
|
|
||||||
void IOMonitor::_interrupt() noexcept {
|
|
||||||
std::unique_lock<std::mutex> run_lock(_run_lock, std::try_to_lock);
|
|
||||||
|
|
||||||
_interrupting = true;
|
|
||||||
|
|
||||||
uint64_t counter = 1;
|
|
||||||
[[maybe_unused]] ssize_t ret = ::write(_event_fd, &counter, sizeof(counter));
|
|
||||||
assert(ret == sizeof(counter));
|
|
||||||
|
|
||||||
// Wait for the IO monitor to _stop
|
|
||||||
std::lock_guard<std::mutex> io_lock(_io_lock);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void IOMonitor::_continue() noexcept {
|
|
||||||
std::unique_lock<std::mutex> run_lock(_run_lock, std::try_to_lock);
|
|
||||||
|
|
||||||
uint64_t counter;
|
|
||||||
[[maybe_unused]] ssize_t ret = ::read(_event_fd, &counter, sizeof(counter));
|
|
||||||
|
|
||||||
assert(ret != -1);
|
|
||||||
|
|
||||||
if (counter == 1) {
|
|
||||||
_interrupting = false;
|
|
||||||
_interrupt_cv.notify_all();
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2022 PixlOne
|
* Copyright 2019-2023 PixlOne
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
@ -21,8 +21,10 @@
|
|||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <map>
|
#include <map>
|
||||||
|
#include <memory>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <condition_variable>
|
#include <condition_variable>
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
namespace logid::backend::raw {
|
namespace logid::backend::raw {
|
||||||
struct IOHandler {
|
struct IOHandler {
|
||||||
@ -39,33 +41,31 @@ namespace logid::backend::raw {
|
|||||||
public:
|
public:
|
||||||
IOMonitor();
|
IOMonitor();
|
||||||
|
|
||||||
|
IOMonitor(IOMonitor&&) = delete;
|
||||||
|
|
||||||
|
IOMonitor(const IOMonitor&) = delete;
|
||||||
|
|
||||||
|
IOMonitor& operator=(IOMonitor&&) = delete;
|
||||||
|
|
||||||
|
IOMonitor& operator=(const IOMonitor&) = delete;
|
||||||
|
|
||||||
~IOMonitor() noexcept;
|
~IOMonitor() noexcept;
|
||||||
|
|
||||||
void add(int fd, IOHandler handler);
|
void add(int fd, IOHandler handler);
|
||||||
|
|
||||||
void remove(int fd) noexcept;
|
void remove(int fd) noexcept;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void _listen(); // This is a blocking call
|
void _listen(); // This is a blocking call
|
||||||
void _stop() noexcept;
|
void _stop() noexcept;
|
||||||
|
std::unique_lock<std::mutex> _yield() noexcept;
|
||||||
[[maybe_unused]]
|
|
||||||
[[nodiscard]] bool _running() const;
|
|
||||||
|
|
||||||
void _interrupt() noexcept;
|
|
||||||
|
|
||||||
void _continue() noexcept;
|
|
||||||
|
|
||||||
std::unique_ptr<std::thread> _io_thread;
|
std::unique_ptr<std::thread> _io_thread;
|
||||||
|
|
||||||
std::map<int, IOHandler> _fds;
|
std::mutex _run_mutex;
|
||||||
std::mutex _io_lock, _ctl_lock;
|
std::mutex _yield_mutex;
|
||||||
mutable std::mutex _run_lock;
|
|
||||||
std::atomic_bool _is_running;
|
|
||||||
|
|
||||||
std::atomic_bool _interrupting;
|
std::map<int, std::shared_ptr<IOHandler>> _fds;
|
||||||
std::mutex _interrupt_mutex;
|
std::atomic_bool _is_running;
|
||||||
std::condition_variable _interrupt_cv;
|
|
||||||
|
|
||||||
const int _epoll_fd;
|
const int _epoll_fd;
|
||||||
const int _event_fd;
|
const int _event_fd;
|
||||||
|
@ -24,6 +24,7 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
#include <system_error>
|
#include <system_error>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
#include <regex>
|
||||||
|
|
||||||
extern "C"
|
extern "C"
|
||||||
{
|
{
|
||||||
@ -32,12 +33,17 @@ extern "C"
|
|||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <sys/ioctl.h>
|
#include <sys/ioctl.h>
|
||||||
#include <linux/hidraw.h>
|
#include <linux/hidraw.h>
|
||||||
|
#include <linux/input.h>
|
||||||
}
|
}
|
||||||
|
|
||||||
using namespace logid::backend::raw;
|
using namespace logid::backend::raw;
|
||||||
using namespace logid::backend;
|
using namespace logid::backend;
|
||||||
using namespace std::chrono;
|
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 get_fd(const std::string& path) {
|
||||||
int fd = ::open(path.c_str(), O_RDWR | O_NONBLOCK);
|
int fd = ::open(path.c_str(), O_RDWR | O_NONBLOCK);
|
||||||
if (fd == -1)
|
if (fd == -1)
|
||||||
@ -56,7 +62,37 @@ RawDevice::dev_info get_dev_info(int fd) {
|
|||||||
"RawDevice HIDIOCGRAWINFO failed");
|
"RawDevice HIDIOCGRAWINFO failed");
|
||||||
}
|
}
|
||||||
|
|
||||||
return {dev_info.vendor, dev_info.product};
|
RawDevice::BusType type = RawDevice::OtherBus;
|
||||||
|
|
||||||
|
switch (dev_info.bustype) {
|
||||||
|
case BUS_USB:
|
||||||
|
type = RawDevice::USB;
|
||||||
|
break;
|
||||||
|
case BUS_BLUETOOTH:
|
||||||
|
type = RawDevice::Bluetooth;
|
||||||
|
break;
|
||||||
|
default:;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return {
|
||||||
|
.vid = dev_info.vendor,
|
||||||
|
.pid = dev_info.product,
|
||||||
|
.bus_type = type
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string get_phys(int fd) {
|
||||||
|
ssize_t len;
|
||||||
|
char buf[256];
|
||||||
|
if (-1 == (len = ::ioctl(fd, HIDIOCGRAWPHYS(sizeof(buf)), buf))) {
|
||||||
|
int err = errno;
|
||||||
|
::close(fd);
|
||||||
|
throw std::system_error(err, std::system_category(),
|
||||||
|
"RawDevice HIDIOCGRAWPHYS failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
return {buf, static_cast<size_t>(len) - 1};
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string get_name(int fd) {
|
std::string get_name(int fd) {
|
||||||
@ -68,7 +104,7 @@ std::string get_name(int fd) {
|
|||||||
throw std::system_error(err, std::system_category(),
|
throw std::system_error(err, std::system_category(),
|
||||||
"RawDevice HIDIOCGRAWNAME failed");
|
"RawDevice HIDIOCGRAWNAME failed");
|
||||||
}
|
}
|
||||||
return {name_buf, static_cast<size_t>(len)};
|
return {name_buf, static_cast<size_t>(len) - 1};
|
||||||
}
|
}
|
||||||
|
|
||||||
RawDevice::RawDevice(std::string path, const std::shared_ptr<DeviceMonitor>& monitor) :
|
RawDevice::RawDevice(std::string path, const std::shared_ptr<DeviceMonitor>& monitor) :
|
||||||
@ -76,10 +112,27 @@ RawDevice::RawDevice(std::string path, const std::shared_ptr<DeviceMonitor>& mon
|
|||||||
_dev_info(get_dev_info(_fd)), _name(get_name(_fd)),
|
_dev_info(get_dev_info(_fd)), _name(get_name(_fd)),
|
||||||
_report_desc(getReportDescriptor(_fd)), _io_monitor(monitor->ioMonitor()),
|
_report_desc(getReportDescriptor(_fd)), _io_monitor(monitor->ioMonitor()),
|
||||||
_event_handlers(std::make_shared<EventHandlerList<RawDevice>>()) {
|
_event_handlers(std::make_shared<EventHandlerList<RawDevice>>()) {
|
||||||
|
|
||||||
|
if (busType() == USB) {
|
||||||
|
auto phys = get_phys(_fd);
|
||||||
|
_sub_device = std::regex_match(phys, virtual_path_regex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void RawDevice::_ready() {
|
||||||
_io_monitor->add(_fd, {
|
_io_monitor->add(_fd, {
|
||||||
[this]() { _readReports(); },
|
[self_weak = _self]() {
|
||||||
[this]() { _valid = false; },
|
if (auto self = self_weak.lock())
|
||||||
[this]() { _valid = false; }
|
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;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -105,6 +158,14 @@ int16_t RawDevice::productId() const {
|
|||||||
return _dev_info.pid;
|
return _dev_info.pid;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
RawDevice::BusType RawDevice::busType() const {
|
||||||
|
return _dev_info.bus_type;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RawDevice::isSubDevice() const {
|
||||||
|
return _sub_device;
|
||||||
|
}
|
||||||
|
|
||||||
std::vector<uint8_t> RawDevice::getReportDescriptor(const std::string& path) {
|
std::vector<uint8_t> RawDevice::getReportDescriptor(const std::string& path) {
|
||||||
int fd = ::open(path.c_str(), O_RDWR | O_NONBLOCK);
|
int fd = ::open(path.c_str(), O_RDWR | O_NONBLOCK);
|
||||||
if (fd == -1)
|
if (fd == -1)
|
||||||
@ -150,15 +211,17 @@ void RawDevice::sendReport(const std::vector<uint8_t>& report) {
|
|||||||
printf("\n");
|
printf("\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (write(_fd, report.data(), report.size()) == -1)
|
|
||||||
throw std::system_error(errno, std::system_category(),
|
for (int i = 0; i < max_write_tries && write(_fd, report.data(), report.size()) == -1; ++i) {
|
||||||
"sendReport write failed");
|
auto err = errno;
|
||||||
|
if (err != EPIPE)
|
||||||
|
throw std::system_error(err, std::system_category(),
|
||||||
|
"sendReport write failed");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
EventHandlerLock<RawDevice> RawDevice::addEventHandler(RawEventHandler handler) {
|
EventHandlerLock<RawDevice> RawDevice::addEventHandler(RawEventHandler handler) {
|
||||||
std::unique_lock<std::shared_mutex> lock(_event_handlers->mutex);
|
return {_event_handlers, _event_handlers->add(std::forward<RawEventHandler>(handler))};
|
||||||
_event_handlers->list.emplace_front(std::move(handler));
|
|
||||||
return {_event_handlers, _event_handlers->list.cbegin()};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void RawDevice::_readReports() {
|
void RawDevice::_readReports() {
|
||||||
@ -181,8 +244,5 @@ void RawDevice::_readReports() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void RawDevice::_handleEvent(const std::vector<uint8_t>& report) {
|
void RawDevice::_handleEvent(const std::vector<uint8_t>& report) {
|
||||||
std::shared_lock<std::shared_mutex> lock(_event_handlers->mutex);
|
_event_handlers->run_all(report);
|
||||||
for (auto& handler : _event_handlers->list)
|
|
||||||
if (handler.condition(report))
|
|
||||||
handler.callback(report);
|
|
||||||
}
|
}
|
||||||
|
@ -34,17 +34,40 @@ namespace logid::backend::raw {
|
|||||||
|
|
||||||
class IOMonitor;
|
class IOMonitor;
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
class RawDeviceWrapper : public T {
|
||||||
|
public:
|
||||||
|
template <typename... Args>
|
||||||
|
RawDeviceWrapper(Args... args) : T(std::forward<Args>(args)...) { }
|
||||||
|
};
|
||||||
|
|
||||||
class RawDevice {
|
class RawDevice {
|
||||||
|
template <typename>
|
||||||
|
friend class RawDeviceWrapper;
|
||||||
public:
|
public:
|
||||||
static constexpr int max_data_length = 32;
|
static constexpr int max_data_length = 32;
|
||||||
typedef RawEventHandler EventHandler;
|
typedef RawEventHandler EventHandler;
|
||||||
|
|
||||||
|
enum BusType {
|
||||||
|
USB,
|
||||||
|
Bluetooth,
|
||||||
|
OtherBus
|
||||||
|
};
|
||||||
|
|
||||||
struct dev_info {
|
struct dev_info {
|
||||||
int16_t vid;
|
int16_t vid;
|
||||||
int16_t pid;
|
int16_t pid;
|
||||||
|
BusType bus_type;
|
||||||
};
|
};
|
||||||
|
|
||||||
RawDevice(std::string path, const std::shared_ptr<DeviceMonitor>& monitor);
|
template <typename... Args>
|
||||||
|
static std::shared_ptr<RawDevice> make(Args... args) {
|
||||||
|
auto raw_dev = std::make_shared<RawDeviceWrapper<RawDevice>>(
|
||||||
|
std::forward<Args>(args)...);
|
||||||
|
raw_dev->_self = raw_dev;
|
||||||
|
raw_dev->_ready();
|
||||||
|
return raw_dev;
|
||||||
|
}
|
||||||
|
|
||||||
~RawDevice() noexcept;
|
~RawDevice() noexcept;
|
||||||
|
|
||||||
@ -57,6 +80,10 @@ namespace logid::backend::raw {
|
|||||||
|
|
||||||
[[nodiscard]] int16_t productId() const;
|
[[nodiscard]] int16_t productId() const;
|
||||||
|
|
||||||
|
[[nodiscard]] BusType busType() const;
|
||||||
|
|
||||||
|
[[nodiscard]] bool isSubDevice() const;
|
||||||
|
|
||||||
static std::vector<uint8_t> getReportDescriptor(const std::string& path);
|
static std::vector<uint8_t> getReportDescriptor(const std::string& path);
|
||||||
|
|
||||||
static std::vector<uint8_t> getReportDescriptor(int fd);
|
static std::vector<uint8_t> getReportDescriptor(int fd);
|
||||||
@ -68,6 +95,10 @@ namespace logid::backend::raw {
|
|||||||
[[nodiscard]] EventHandlerLock<RawDevice> addEventHandler(RawEventHandler handler);
|
[[nodiscard]] EventHandlerLock<RawDevice> addEventHandler(RawEventHandler handler);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
RawDevice(std::string path, const std::shared_ptr<DeviceMonitor>& monitor);
|
||||||
|
|
||||||
|
void _ready();
|
||||||
|
|
||||||
void _readReports();
|
void _readReports();
|
||||||
|
|
||||||
std::atomic_bool _valid;
|
std::atomic_bool _valid;
|
||||||
@ -80,6 +111,10 @@ namespace logid::backend::raw {
|
|||||||
|
|
||||||
std::shared_ptr<IOMonitor> _io_monitor;
|
std::shared_ptr<IOMonitor> _io_monitor;
|
||||||
|
|
||||||
|
std::weak_ptr<RawDevice> _self;
|
||||||
|
|
||||||
|
bool _sub_device = false;
|
||||||
|
|
||||||
std::shared_ptr<EventHandlerList<RawDevice>> _event_handlers;
|
std::shared_ptr<EventHandlerList<RawDevice>> _event_handlers;
|
||||||
|
|
||||||
void _handleEvent(const std::vector<uint8_t>& report);
|
void _handleEvent(const std::vector<uint8_t>& report);
|
||||||
|
@ -16,11 +16,15 @@
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <config/types.h>
|
#include <config/schema.h>
|
||||||
#include <util/log.h>
|
#include <util/log.h>
|
||||||
|
|
||||||
using namespace logid;
|
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) {
|
void config::logError(const libconfig::Setting& setting, std::exception& e) {
|
||||||
logPrintf(WARN, "Error at line %d: %s", setting.getSourceLine(), e.what());
|
logPrintf(WARN, "Error at line %d: %s", setting.getSourceLine(), e.what());
|
||||||
}
|
}
|
@ -22,6 +22,7 @@
|
|||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
namespace logid::config {
|
namespace logid::config {
|
||||||
template<typename T>
|
template<typename T>
|
||||||
@ -38,55 +39,45 @@ namespace logid::config {
|
|||||||
template<typename T>
|
template<typename T>
|
||||||
void append(libconfig::Setting& list, const T& t);
|
void append(libconfig::Setting& list, const T& t);
|
||||||
|
|
||||||
namespace {
|
template<typename T, typename... M>
|
||||||
template<typename T, typename... M>
|
struct group_io {
|
||||||
struct group_io {
|
};
|
||||||
};
|
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
struct group_io<T> {
|
struct group_io<T> {
|
||||||
static inline void get(
|
static void get(const libconfig::Setting&, T*,
|
||||||
[[maybe_unused]] const libconfig::Setting& s,
|
const std::vector<std::string>&, const std::size_t) {}
|
||||||
[[maybe_unused]] T* t,
|
|
||||||
[[maybe_unused]] const std::vector<std::string>& names,
|
|
||||||
[[maybe_unused]] const std::size_t index) {}
|
|
||||||
|
|
||||||
static inline void set(
|
static void set(libconfig::Setting&, const T*,
|
||||||
[[maybe_unused]] libconfig::Setting& s,
|
const std::vector<std::string>&, const std::size_t) {}
|
||||||
[[maybe_unused]] const T* t,
|
};
|
||||||
[[maybe_unused]] const std::vector<std::string>& names,
|
|
||||||
[[maybe_unused]] const std::size_t index) {}
|
|
||||||
};
|
|
||||||
|
|
||||||
template<typename T, typename A, typename... M>
|
template<typename T, typename A, typename... M>
|
||||||
struct group_io<T, A, M...> {
|
struct group_io<T, A, M...> {
|
||||||
static inline void get(
|
static void get(const libconfig::Setting& s, T* t,
|
||||||
const libconfig::Setting& s, T* t,
|
const std::vector<std::string>& names,
|
||||||
const std::vector<std::string>& names,
|
const std::size_t index, A T::* arg, M T::*... rest) {
|
||||||
const std::size_t index, A T::* arg, M T::*... rest) {
|
auto& x = t->*(arg);
|
||||||
auto& x = t->*(arg);
|
A old{x};
|
||||||
A old{x};
|
try {
|
||||||
try {
|
x = config::get<A>(s, names[index]);
|
||||||
x = config::get<A>(s, names[index]);
|
group_io<T, M...>::get(s, t, names, index + 1, rest...);
|
||||||
group_io<T, M...>::get(s, t, names, index + 1, rest...);
|
} catch (libconfig::SettingTypeException& e) {
|
||||||
} catch (libconfig::SettingTypeException& e) {
|
x = old;
|
||||||
x = old;
|
throw;
|
||||||
throw;
|
} catch (libconfig::SettingException& e) {
|
||||||
} catch (libconfig::SettingException& e) {
|
x = old;
|
||||||
x = old;
|
throw libconfig::SettingTypeException(s);
|
||||||
throw libconfig::SettingTypeException(s);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static inline void set(
|
static void set(libconfig::Setting& s, const T* t,
|
||||||
libconfig::Setting& s, const T* t,
|
const std::vector<std::string>& names,
|
||||||
const std::vector<std::string>& names,
|
const std::size_t index, A T::* arg, M T::*... rest) {
|
||||||
const std::size_t index, A T::* arg, M T::*... rest) {
|
config::set(s, names[index], t->*(arg));
|
||||||
config::set(s, names[index], t->*(arg));
|
group_io<T, M...>::set(s, t, names, index + 1, rest...);
|
||||||
group_io<T, M...>::set(s, t, names, index + 1, rest...);
|
}
|
||||||
}
|
};
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename Sign>
|
template<typename Sign>
|
||||||
struct signed_group;
|
struct signed_group;
|
||||||
@ -149,22 +140,19 @@ namespace logid::config {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
namespace {
|
template<typename T>
|
||||||
template<typename T>
|
struct normalize_signature {
|
||||||
struct normalize_signature {
|
static const T& make(const T& ret) { return ret; }
|
||||||
static inline const T& make(const T& ret) { return ret; }
|
};
|
||||||
};
|
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
struct normalize_signature<std::string> {
|
struct normalize_signature<std::string> {
|
||||||
static inline std::string make(const std::string& data) {
|
static std::string make(const std::string& data) {
|
||||||
std::string ret = data;
|
std::string ret = data;
|
||||||
std::transform(ret.begin(), ret.end(),
|
std::transform(ret.begin(), ret.end(), ret.begin(), ::tolower);
|
||||||
ret.begin(), ::tolower);
|
return ret;
|
||||||
return ret;
|
}
|
||||||
}
|
};
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename Sign>
|
template<typename Sign>
|
||||||
struct signed_group : public group {
|
struct signed_group : public group {
|
||||||
|
@ -24,13 +24,11 @@
|
|||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
namespace logid::config {
|
namespace logid::config {
|
||||||
template<size_t N>
|
struct string_literal { };
|
||||||
struct string_literal {
|
|
||||||
constexpr string_literal(const char (& str)[N]) {
|
|
||||||
std::copy_n(str, N, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
char value[N]{};
|
template<const char* str>
|
||||||
|
struct string_literal_of : public string_literal {
|
||||||
|
constexpr static const char* value = str;
|
||||||
};
|
};
|
||||||
|
|
||||||
template<class T>
|
template<class T>
|
||||||
@ -46,10 +44,12 @@ namespace logid::config {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Warning: map must be a variant of groups or a group
|
// Warning: map must be a variant of groups or a group
|
||||||
template<typename K, typename V, string_literal KeyName,
|
template<typename K, typename V, typename KeyName,
|
||||||
typename Compare=typename std::map<K, V>::key_compare,
|
typename Compare=typename std::map<K, V>::key_compare,
|
||||||
typename Allocator=typename std::map<K, V>::allocator_type>
|
typename Allocator=typename std::map<K, V>::allocator_type>
|
||||||
class map : public std::map<K, V, Compare, Allocator> {
|
class map : public std::map<K, V, Compare, Allocator> {
|
||||||
|
static_assert(std::is_base_of<string_literal, KeyName>::value,
|
||||||
|
"KeyName must be a string_literal");
|
||||||
public:
|
public:
|
||||||
template<typename... Args>
|
template<typename... Args>
|
||||||
explicit map(Args... args) :
|
explicit map(Args... args) :
|
||||||
|
@ -51,6 +51,11 @@ namespace logid::actions {
|
|||||||
}
|
}
|
||||||
|
|
||||||
namespace logid::config {
|
namespace logid::config {
|
||||||
|
struct keys {
|
||||||
|
static const char name[];
|
||||||
|
static const char cid[];
|
||||||
|
static const char direction[];
|
||||||
|
};
|
||||||
|
|
||||||
struct NoAction : public signed_group<std::string> {
|
struct NoAction : public signed_group<std::string> {
|
||||||
typedef actions::NullAction action;
|
typedef actions::NullAction action;
|
||||||
@ -213,7 +218,7 @@ namespace logid::config {
|
|||||||
|
|
||||||
struct GestureAction : public signed_group<std::string> {
|
struct GestureAction : public signed_group<std::string> {
|
||||||
typedef actions::GestureAction action;
|
typedef actions::GestureAction action;
|
||||||
std::optional<map<std::string, Gesture, "direction",
|
std::optional<map<std::string, Gesture, string_literal_of<keys::direction>,
|
||||||
less_caseless<std::string>>> gestures;
|
less_caseless<std::string>>> gestures;
|
||||||
|
|
||||||
GestureAction() : signed_group<std::string>(
|
GestureAction() : signed_group<std::string>(
|
||||||
@ -244,9 +249,10 @@ namespace logid::config {
|
|||||||
struct SmartShift : public group {
|
struct SmartShift : public group {
|
||||||
std::optional<bool> on;
|
std::optional<bool> on;
|
||||||
std::optional<unsigned int> threshold;
|
std::optional<unsigned int> threshold;
|
||||||
|
std::optional<unsigned int> torque;
|
||||||
|
|
||||||
SmartShift() : group({"on", "threshold"},
|
SmartShift() : group({"on", "threshold", "torque"},
|
||||||
&SmartShift::on, &SmartShift::threshold) {}
|
&SmartShift::on, &SmartShift::threshold, &SmartShift::torque) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@ -284,7 +290,7 @@ namespace logid::config {
|
|||||||
&ThumbWheel::tap) {}
|
&ThumbWheel::tap) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef map<uint16_t, Button, "cid"> RemapButton;
|
typedef map<uint16_t, Button, string_literal_of<keys::cid>> RemapButton;
|
||||||
|
|
||||||
struct Profile : public group {
|
struct Profile : public group {
|
||||||
std::optional<DPI> dpi;
|
std::optional<DPI> dpi;
|
||||||
@ -302,7 +308,7 @@ namespace logid::config {
|
|||||||
|
|
||||||
struct Device : public group {
|
struct Device : public group {
|
||||||
ipcgull::property<std::string> default_profile;
|
ipcgull::property<std::string> default_profile;
|
||||||
map<std::string, Profile, "name"> profiles;
|
map<std::string, Profile, string_literal_of<keys::name>> profiles;
|
||||||
|
|
||||||
Device() : group({"default_profile", "profiles"},
|
Device() : group({"default_profile", "profiles"},
|
||||||
&Device::default_profile,
|
&Device::default_profile,
|
||||||
@ -313,7 +319,7 @@ namespace logid::config {
|
|||||||
|
|
||||||
struct Config : public group {
|
struct Config : public group {
|
||||||
std::optional<map<std::string,
|
std::optional<map<std::string,
|
||||||
std::variant<Device, Profile>, "name">> devices;
|
std::variant<Device, Profile>, string_literal_of<keys::name>>> devices;
|
||||||
std::optional<std::set<uint16_t>> ignore;
|
std::optional<std::set<uint16_t>> ignore;
|
||||||
std::optional<double> io_timeout;
|
std::optional<double> io_timeout;
|
||||||
std::optional<int> workers;
|
std::optional<int> workers;
|
||||||
|
@ -33,416 +33,402 @@
|
|||||||
namespace logid::config {
|
namespace logid::config {
|
||||||
void logError(const libconfig::Setting& setting, std::exception& e);
|
void logError(const libconfig::Setting& setting, std::exception& e);
|
||||||
|
|
||||||
namespace {
|
template<typename T>
|
||||||
template<typename T>
|
struct config_io {
|
||||||
struct config_io {
|
static_assert(std::is_base_of<group, T>::value);
|
||||||
static_assert(std::is_base_of<group, T>::value);
|
|
||||||
|
|
||||||
static inline T get(const libconfig::Setting& parent,
|
static T get(const libconfig::Setting& parent,
|
||||||
const std::string& name) {
|
const std::string& name) {
|
||||||
T t{};
|
T t{};
|
||||||
t._load(parent.lookup(name));
|
t._load(parent.lookup(name));
|
||||||
return t;
|
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 inline T get(const libconfig::Setting& setting) {
|
static void set(libconfig::Setting& setting, const T& t) {
|
||||||
T t{};
|
t._save(setting);
|
||||||
t._load(setting);
|
}
|
||||||
return t;
|
|
||||||
|
static void append(libconfig::Setting& list, const T& t) {
|
||||||
|
auto& x = list.add(libconfig::Setting::TypeGroup);
|
||||||
|
set(x, t);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
struct config_io<ipcgull::property<T>> : public config_io<T> {
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename T, libconfig::Setting::Type TypeEnum>
|
||||||
|
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 inline void set(libconfig::Setting& parent,
|
static void set(libconfig::Setting& setting, const T& t) {
|
||||||
const std::string& name,
|
setting = t;
|
||||||
const T& t) {
|
}
|
||||||
if (!parent.exists(name)) {
|
|
||||||
parent.add(name, libconfig::Setting::TypeGroup);
|
static void append(libconfig::Setting& list, const T& t) {
|
||||||
} else if (parent.lookup(name).getType()
|
auto& x = list.add(TypeEnum);
|
||||||
!= libconfig::Setting::TypeGroup) {
|
set(x, t);
|
||||||
parent.remove(name);
|
}
|
||||||
parent.add(name, libconfig::Setting::TypeGroup);
|
};
|
||||||
}
|
|
||||||
t._save(parent.lookup(name));
|
template<typename T, typename O, libconfig::Setting::Type TypeEnum>
|
||||||
|
struct reinterpret_io {
|
||||||
|
static T get(const libconfig::Setting& parent,
|
||||||
|
const std::string& name) {
|
||||||
|
return static_cast<T>(primitive_io<O, TypeEnum>::get(parent, name));
|
||||||
|
}
|
||||||
|
|
||||||
|
static T get(const libconfig::Setting& setting) {
|
||||||
|
return static_cast<T>(primitive_io<O, TypeEnum>::get(setting));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void set(libconfig::Setting& parent,
|
||||||
|
const std::string& name,
|
||||||
|
const T& t) {
|
||||||
|
primitive_io<O, TypeEnum>::set(parent, name,
|
||||||
|
static_cast<O>(t));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void set(libconfig::Setting& setting, const T& t) {
|
||||||
|
primitive_io<O, TypeEnum>::set(setting,
|
||||||
|
static_cast<O>(t));
|
||||||
|
}
|
||||||
|
|
||||||
|
[[maybe_unused]]
|
||||||
|
static void append(libconfig::Setting& list, const T& t) {
|
||||||
|
primitive_io<O, TypeEnum>::append(list,
|
||||||
|
static_cast<O>(t));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<>
|
||||||
|
struct config_io<bool> : public primitive_io<bool,
|
||||||
|
libconfig::Setting::TypeBoolean> {
|
||||||
|
};
|
||||||
|
template<>
|
||||||
|
struct config_io<int8_t> : public reinterpret_io<int8_t, int,
|
||||||
|
libconfig::Setting::TypeInt> {
|
||||||
|
};
|
||||||
|
template<>
|
||||||
|
struct config_io<uint8_t> : public reinterpret_io<uint8_t, int,
|
||||||
|
libconfig::Setting::TypeInt> {
|
||||||
|
};
|
||||||
|
template<>
|
||||||
|
struct config_io<short> : public reinterpret_io<short, int,
|
||||||
|
libconfig::Setting::TypeInt> {
|
||||||
|
};
|
||||||
|
template<>
|
||||||
|
struct config_io<unsigned short> : public reinterpret_io<unsigned short,
|
||||||
|
int, libconfig::Setting::TypeInt> {
|
||||||
|
};
|
||||||
|
template<>
|
||||||
|
struct config_io<int> : public primitive_io<int,
|
||||||
|
libconfig::Setting::TypeInt> {
|
||||||
|
};
|
||||||
|
template<>
|
||||||
|
struct config_io<unsigned int> : public reinterpret_io<unsigned int,
|
||||||
|
int, libconfig::Setting::TypeInt> {
|
||||||
|
};
|
||||||
|
template<>
|
||||||
|
struct config_io<long> : public reinterpret_io<long, long long,
|
||||||
|
libconfig::Setting::TypeInt64> {
|
||||||
|
};
|
||||||
|
template<>
|
||||||
|
struct config_io<unsigned long> : public reinterpret_io<unsigned long,
|
||||||
|
long long, libconfig::Setting::TypeInt64> {
|
||||||
|
};
|
||||||
|
template<>
|
||||||
|
struct config_io<long long> : public primitive_io<long long,
|
||||||
|
libconfig::Setting::TypeInt64> {
|
||||||
|
};
|
||||||
|
template<>
|
||||||
|
struct config_io<unsigned long long> :
|
||||||
|
public reinterpret_io<unsigned long long, long long,
|
||||||
|
libconfig::Setting::TypeInt64> {
|
||||||
|
};
|
||||||
|
template<>
|
||||||
|
struct config_io<float> : public primitive_io<float,
|
||||||
|
libconfig::Setting::TypeFloat> {
|
||||||
|
};
|
||||||
|
template<>
|
||||||
|
struct config_io<double> : public primitive_io<double,
|
||||||
|
libconfig::Setting::TypeFloat> {
|
||||||
|
};
|
||||||
|
template<>
|
||||||
|
struct config_io<std::string> : public primitive_io<std::string,
|
||||||
|
libconfig::Setting::TypeString> {
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename... T>
|
||||||
|
struct config_io<std::variant<T...>> {
|
||||||
|
private:
|
||||||
|
template<typename Singleton>
|
||||||
|
static std::variant<T...> try_each(const libconfig::Setting& setting) {
|
||||||
|
return config_io<Singleton>::get(setting);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename First, typename Next, typename... Rest>
|
||||||
|
static std::variant<T...> try_each(const libconfig::Setting& setting) {
|
||||||
|
try {
|
||||||
|
return config_io<First>::get(setting);
|
||||||
|
} catch (libconfig::SettingException& e) {
|
||||||
|
return try_each<Next, Rest...>(setting);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static inline void set(libconfig::Setting& setting, const T& t) {
|
public:
|
||||||
t._save(setting);
|
static std::variant<T...> get(const libconfig::Setting& setting) {
|
||||||
}
|
return try_each<T...>(setting);
|
||||||
|
}
|
||||||
|
|
||||||
static inline void append(libconfig::Setting& list, const T& t) {
|
static std::variant<T...> get(const libconfig::Setting& parent,
|
||||||
auto& x = list.add(libconfig::Setting::TypeGroup);
|
const std::string& name) {
|
||||||
set(x, t);
|
return get(parent.lookup(name));
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
template<typename T>
|
static void set(libconfig::Setting& setting,
|
||||||
struct config_io<ipcgull::property<T>> : public config_io<T> {
|
const std::variant<T...>& t) {
|
||||||
};
|
std::visit([&setting](auto&& arg) {
|
||||||
|
config::set(setting, arg);
|
||||||
|
}, t);
|
||||||
|
}
|
||||||
|
|
||||||
template<typename T, libconfig::Setting::Type TypeEnum>
|
static void set(libconfig::Setting& parent,
|
||||||
struct primitive_io {
|
const std::string& name,
|
||||||
static inline T get(const libconfig::Setting& parent,
|
const std::variant<T...>& t) {
|
||||||
const std::string& name) {
|
std::visit([&parent, &name](auto&& arg) {
|
||||||
return parent.lookup(name);
|
config::set(parent, name, arg);
|
||||||
}
|
}, t);
|
||||||
|
}
|
||||||
|
|
||||||
static inline T get(const libconfig::Setting& setting) {
|
[[maybe_unused]]
|
||||||
return setting;
|
static void append(libconfig::Setting& list, const std::variant<T...>& t) {
|
||||||
}
|
std::visit([&list](auto&& arg) {
|
||||||
|
config::append(list, arg);
|
||||||
|
}, t);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
static inline void set(libconfig::Setting& parent,
|
template<typename T>
|
||||||
const std::string& name,
|
struct config_io<std::list<T>> {
|
||||||
const T& t) {
|
static std::list<T> get(const libconfig::Setting& setting) {
|
||||||
if (!parent.exists(name)) {
|
const auto size = setting.getLength();
|
||||||
parent.add(name, TypeEnum);
|
std::list<T> t{};
|
||||||
} else if (parent.lookup(name).getType() != TypeEnum) {
|
for (int i = 0; i < size; ++i) {
|
||||||
parent.remove(name);
|
|
||||||
parent.add(name, TypeEnum);
|
|
||||||
}
|
|
||||||
set(parent.lookup(name), t);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void set(libconfig::Setting& setting, const T& t) {
|
|
||||||
setting = t;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void append(libconfig::Setting& list, const T& t) {
|
|
||||||
auto& x = list.add(TypeEnum);
|
|
||||||
set(x, t);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template<typename T, typename O, libconfig::Setting::Type TypeEnum>
|
|
||||||
struct reinterpret_io {
|
|
||||||
static inline T get(const libconfig::Setting& parent,
|
|
||||||
const std::string& name) {
|
|
||||||
return static_cast<T>(primitive_io<O, TypeEnum>::get(parent, name));
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline T get(const libconfig::Setting& setting) {
|
|
||||||
return static_cast<T>(primitive_io<O, TypeEnum>::get(setting));
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void set(libconfig::Setting& parent,
|
|
||||||
const std::string& name,
|
|
||||||
const T& t) {
|
|
||||||
primitive_io<O, TypeEnum>::set(parent, name,
|
|
||||||
static_cast<O>(t));
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void set(libconfig::Setting& setting, const T& t) {
|
|
||||||
primitive_io<O, TypeEnum>::set(setting,
|
|
||||||
static_cast<O>(t));
|
|
||||||
}
|
|
||||||
|
|
||||||
[[maybe_unused]]
|
|
||||||
static inline void append(libconfig::Setting& list, const T& t) {
|
|
||||||
primitive_io<O, TypeEnum>::append(list,
|
|
||||||
static_cast<O>(t));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template<>
|
|
||||||
struct config_io<bool> : public primitive_io<bool,
|
|
||||||
libconfig::Setting::TypeBoolean> {
|
|
||||||
};
|
|
||||||
template<>
|
|
||||||
struct config_io<int8_t> : public reinterpret_io<int8_t, int,
|
|
||||||
libconfig::Setting::TypeInt> {
|
|
||||||
};
|
|
||||||
template<>
|
|
||||||
struct config_io<uint8_t> : public reinterpret_io<uint8_t, int,
|
|
||||||
libconfig::Setting::TypeInt> {
|
|
||||||
};
|
|
||||||
template<>
|
|
||||||
struct config_io<short> : public reinterpret_io<short, int,
|
|
||||||
libconfig::Setting::TypeInt> {
|
|
||||||
};
|
|
||||||
template<>
|
|
||||||
struct config_io<unsigned short> : public reinterpret_io<unsigned short,
|
|
||||||
int, libconfig::Setting::TypeInt> {
|
|
||||||
};
|
|
||||||
template<>
|
|
||||||
struct config_io<int> : public primitive_io<int,
|
|
||||||
libconfig::Setting::TypeInt> {
|
|
||||||
};
|
|
||||||
template<>
|
|
||||||
struct config_io<unsigned int> : public reinterpret_io<unsigned int,
|
|
||||||
int, libconfig::Setting::TypeInt> {
|
|
||||||
};
|
|
||||||
template<>
|
|
||||||
struct config_io<long> : public reinterpret_io<long, long long,
|
|
||||||
libconfig::Setting::TypeInt64> {
|
|
||||||
};
|
|
||||||
template<>
|
|
||||||
struct config_io<unsigned long> : public reinterpret_io<unsigned long,
|
|
||||||
long long, libconfig::Setting::TypeInt64> {
|
|
||||||
};
|
|
||||||
template<>
|
|
||||||
struct config_io<long long> : public primitive_io<long long,
|
|
||||||
libconfig::Setting::TypeInt64> {
|
|
||||||
};
|
|
||||||
template<>
|
|
||||||
struct config_io<unsigned long long> :
|
|
||||||
public reinterpret_io<unsigned long long, long long,
|
|
||||||
libconfig::Setting::TypeInt64> {
|
|
||||||
};
|
|
||||||
template<>
|
|
||||||
struct config_io<float> : public primitive_io<float,
|
|
||||||
libconfig::Setting::TypeFloat> {
|
|
||||||
};
|
|
||||||
template<>
|
|
||||||
struct config_io<double> : public primitive_io<double,
|
|
||||||
libconfig::Setting::TypeFloat> {
|
|
||||||
};
|
|
||||||
template<>
|
|
||||||
struct config_io<std::string> : public primitive_io<std::string,
|
|
||||||
libconfig::Setting::TypeString> {
|
|
||||||
};
|
|
||||||
|
|
||||||
template<typename... T>
|
|
||||||
struct config_io<std::variant<T...>> {
|
|
||||||
private:
|
|
||||||
template<typename Singleton>
|
|
||||||
static inline std::variant<T...> try_each(
|
|
||||||
const libconfig::Setting& setting) {
|
|
||||||
return config_io<Singleton>::get(setting);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename First, typename Next, typename... Rest>
|
|
||||||
static inline std::variant<T...> try_each(
|
|
||||||
const libconfig::Setting& setting) {
|
|
||||||
try {
|
try {
|
||||||
return config_io<First>::get(setting);
|
t.emplace_back(config_io<T>::get(setting[i]));
|
||||||
|
} catch (libconfig::SettingException& e) {}
|
||||||
|
}
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::list<T> get(const libconfig::Setting& parent, const std::string& name) {
|
||||||
|
return get(parent.lookup(name));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void set(libconfig::Setting& setting, const std::list<T>& t) {
|
||||||
|
while (setting.getLength() != 0)
|
||||||
|
setting.remove((int) 0);
|
||||||
|
for (auto& x: t) {
|
||||||
|
config_io<T>::append(setting, x);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void set(libconfig::Setting& parent,
|
||||||
|
const std::string& name,
|
||||||
|
const std::list<T>& 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>& t) {
|
||||||
|
auto& s = list.add(libconfig::Setting::TypeList);
|
||||||
|
set(s, t);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
struct config_io<std::set<T>> {
|
||||||
|
static std::set<T> get(const libconfig::Setting& setting) {
|
||||||
|
const auto size = setting.getLength();
|
||||||
|
std::set<T> t;
|
||||||
|
for (int i = 0; i < size; ++i) {
|
||||||
|
try {
|
||||||
|
t.emplace(config_io<T>::get(setting[i]));
|
||||||
|
} catch (libconfig::SettingException& e) {}
|
||||||
|
}
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::set<T> get(const libconfig::Setting& parent, const std::string& name) {
|
||||||
|
return get(parent.lookup(name));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void set(libconfig::Setting& setting, const std::set<T>& t) {
|
||||||
|
while (setting.getLength() != 0)
|
||||||
|
setting.remove((int) 0);
|
||||||
|
for (auto& x: t) {
|
||||||
|
auto& s = setting.add(libconfig::Setting::TypeGroup);
|
||||||
|
config_io<T>::set(s, x);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void set(libconfig::Setting& parent,
|
||||||
|
const std::string& name,
|
||||||
|
const std::set<T>& 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>& t) {
|
||||||
|
auto& s = list.add(libconfig::Setting::TypeList);
|
||||||
|
set(s, t);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename K, typename V, typename KeyName,
|
||||||
|
typename Cmp, typename Alloc>
|
||||||
|
struct config_io<map<K, V, KeyName, Cmp, Alloc>> {
|
||||||
|
static map<K, V, KeyName, Cmp, Alloc> get(const libconfig::Setting& setting) {
|
||||||
|
const auto size = setting.getLength();
|
||||||
|
map<K, V, KeyName, Cmp, Alloc> t;
|
||||||
|
for (int i = 0; i < size; ++i) {
|
||||||
|
auto& s = setting[i];
|
||||||
|
try {
|
||||||
|
t.emplace(config_io<K>::get(s.lookup(KeyName::value)),
|
||||||
|
config_io<V>::get(s));
|
||||||
|
} catch (libconfig::SettingException& e) {}
|
||||||
|
}
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
|
||||||
|
static map<K, V, KeyName, Cmp, Alloc> get(
|
||||||
|
const libconfig::Setting& parent, const std::string& name) {
|
||||||
|
return get(parent.lookup(name));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void set(libconfig::Setting& setting,
|
||||||
|
const map<K, V, KeyName, Cmp, Alloc>& t) {
|
||||||
|
while (setting.getLength() != 0)
|
||||||
|
setting.remove((int) 0);
|
||||||
|
for (auto& x: t) {
|
||||||
|
auto& s = setting.add(libconfig::Setting::TypeGroup);
|
||||||
|
config_io<V>::set(s, x.second);
|
||||||
|
config_io<K>::set(s, KeyName::value, x.first);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void set(libconfig::Setting& parent,
|
||||||
|
const std::string& name,
|
||||||
|
const map<K, V, KeyName, Cmp, Alloc>& 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<K, V, KeyName, Cmp, Alloc>& t) {
|
||||||
|
auto& s = list.add(libconfig::Setting::TypeList);
|
||||||
|
set(s, t);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
struct config_io<std::optional<T>> {
|
||||||
|
static std::optional<T> get(const libconfig::Setting& parent,
|
||||||
|
const std::string& name) {
|
||||||
|
if (parent.exists(name)) {
|
||||||
|
auto& setting = parent.lookup(name);
|
||||||
|
try {
|
||||||
|
return config_io<T>::get(setting);
|
||||||
} catch (libconfig::SettingException& e) {
|
} catch (libconfig::SettingException& e) {
|
||||||
return try_each<Next, Rest...>(setting);
|
logError(setting, e);
|
||||||
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public:
|
static void set(libconfig::Setting& parent,
|
||||||
static inline std::variant<T...> get(
|
const std::string& name,
|
||||||
const libconfig::Setting& setting) {
|
const std::optional<T>& t) {
|
||||||
return try_each<T...>(setting);
|
if (t.has_value())
|
||||||
}
|
config_io<T>::set(parent, name, t.value());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
static inline std::variant<T...> get(
|
// Optionals may not appear as part of a list or array
|
||||||
const libconfig::Setting& parent,
|
template<typename T, typename... Rest>
|
||||||
const std::string& name) {
|
struct config_io<std::variant<std::optional<T>, Rest...>> {
|
||||||
return get(parent.lookup(name));
|
static_assert(!sizeof(std::optional<T>), "Invalid type");
|
||||||
}
|
};
|
||||||
|
|
||||||
static inline void set(libconfig::Setting& setting,
|
template<typename T>
|
||||||
const std::variant<T...>& t) {
|
struct config_io<std::list<std::optional<T>>> {
|
||||||
std::visit([&setting](auto&& arg) {
|
static_assert(!sizeof(std::optional<T>), "Invalid type");
|
||||||
config::set(setting, arg);
|
};
|
||||||
}, t);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void set(libconfig::Setting& parent,
|
template<typename T>
|
||||||
const std::string& name,
|
struct config_io<std::optional<std::optional<T>>> {
|
||||||
const std::variant<T...>& t) {
|
static_assert(!sizeof(std::optional<T>), "Invalid type");
|
||||||
std::visit([&parent, &name](auto&& arg) {
|
};
|
||||||
config::set(parent, name, arg);
|
|
||||||
}, t);
|
|
||||||
}
|
|
||||||
|
|
||||||
[[maybe_unused]]
|
|
||||||
static inline void append(libconfig::Setting& list,
|
|
||||||
const std::variant<T...>& t) {
|
|
||||||
std::visit([&list](auto&& arg) {
|
|
||||||
config::append(list, arg);
|
|
||||||
}, t);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
struct config_io<std::list<T>> {
|
|
||||||
static inline std::list<T> get(const libconfig::Setting& setting) {
|
|
||||||
const auto size = setting.getLength();
|
|
||||||
std::list<T> t{};
|
|
||||||
for (int i = 0; i < size; ++i) {
|
|
||||||
try {
|
|
||||||
t.emplace_back(config_io<T>::get(setting[i]));
|
|
||||||
} catch (libconfig::SettingException& e) {}
|
|
||||||
}
|
|
||||||
return t;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline std::list<T> get(const libconfig::Setting& parent,
|
|
||||||
const std::string& name) {
|
|
||||||
return get(parent.lookup(name));
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void set(libconfig::Setting& setting,
|
|
||||||
const std::list<T>& t) {
|
|
||||||
while (setting.getLength() != 0)
|
|
||||||
setting.remove((int) 0);
|
|
||||||
for (auto& x: t) {
|
|
||||||
config_io<T>::append(setting, x);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void set(libconfig::Setting& parent,
|
|
||||||
const std::string& name,
|
|
||||||
const std::list<T>& 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 inline void append(libconfig::Setting& list,
|
|
||||||
const std::list<T>& t) {
|
|
||||||
auto& s = list.add(libconfig::Setting::TypeList);
|
|
||||||
set(s, t);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
struct config_io<std::set<T>> {
|
|
||||||
static inline std::set<T> get(const libconfig::Setting& setting) {
|
|
||||||
const auto size = setting.getLength();
|
|
||||||
std::set<T> t;
|
|
||||||
for (int i = 0; i < size; ++i) {
|
|
||||||
try {
|
|
||||||
t.emplace(config_io<T>::get(setting[i]));
|
|
||||||
} catch (libconfig::SettingException& e) {}
|
|
||||||
}
|
|
||||||
return t;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline std::set<T> get(const libconfig::Setting& parent,
|
|
||||||
const std::string& name) {
|
|
||||||
return get(parent.lookup(name));
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void set(libconfig::Setting& setting,
|
|
||||||
const std::set<T>& t) {
|
|
||||||
while (setting.getLength() != 0)
|
|
||||||
setting.remove((int) 0);
|
|
||||||
for (auto& x: t) {
|
|
||||||
auto& s = setting.add(libconfig::Setting::TypeGroup);
|
|
||||||
config_io<T>::set(s, x);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void set(libconfig::Setting& parent,
|
|
||||||
const std::string& name,
|
|
||||||
const std::set<T>& 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 inline void append(libconfig::Setting& list,
|
|
||||||
const std::set<T>& t) {
|
|
||||||
auto& s = list.add(libconfig::Setting::TypeList);
|
|
||||||
set(s, t);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template<typename K, typename V, string_literal KeyName,
|
|
||||||
typename Cmp, typename Alloc>
|
|
||||||
struct config_io<map<K, V, KeyName, Cmp, Alloc>> {
|
|
||||||
static inline map<K, V, KeyName, Cmp, Alloc> get(
|
|
||||||
const libconfig::Setting& setting) {
|
|
||||||
const auto size = setting.getLength();
|
|
||||||
map<K, V, KeyName, Cmp, Alloc> t;
|
|
||||||
for (int i = 0; i < size; ++i) {
|
|
||||||
auto& s = setting[i];
|
|
||||||
try {
|
|
||||||
t.emplace(config_io<K>::get(s.lookup(KeyName.value)),
|
|
||||||
config_io<V>::get(s));
|
|
||||||
} catch (libconfig::SettingException& e) {}
|
|
||||||
}
|
|
||||||
return t;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline map<K, V, KeyName, Cmp, Alloc> get(
|
|
||||||
const libconfig::Setting& parent, const std::string& name) {
|
|
||||||
return get(parent.lookup(name));
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void set(libconfig::Setting& setting,
|
|
||||||
const map<K, V, KeyName, Cmp, Alloc>& t) {
|
|
||||||
while (setting.getLength() != 0)
|
|
||||||
setting.remove((int) 0);
|
|
||||||
for (auto& x: t) {
|
|
||||||
auto& s = setting.add(libconfig::Setting::TypeGroup);
|
|
||||||
config_io<V>::set(s, x.second);
|
|
||||||
config_io<K>::set(s, KeyName.value, x.first);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void set(libconfig::Setting& parent,
|
|
||||||
const std::string& name,
|
|
||||||
const map<K, V, KeyName, Cmp, Alloc>& 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 inline void append(libconfig::Setting& list,
|
|
||||||
const map<K, V, KeyName, Cmp, Alloc>& t) {
|
|
||||||
auto& s = list.add(libconfig::Setting::TypeList);
|
|
||||||
set(s, t);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
struct config_io<std::optional<T>> {
|
|
||||||
static inline std::optional<T> get(const libconfig::Setting& parent,
|
|
||||||
const std::string& name) {
|
|
||||||
if (parent.exists(name)) {
|
|
||||||
auto& setting = parent.lookup(name);
|
|
||||||
try {
|
|
||||||
return config_io<T>::get(setting);
|
|
||||||
} catch (libconfig::SettingException& e) {
|
|
||||||
logError(setting, e);
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void set(libconfig::Setting& parent,
|
|
||||||
const std::string& name,
|
|
||||||
const std::optional<T>& t) {
|
|
||||||
if (t.has_value())
|
|
||||||
config_io<T>::set(parent, name, t.value());
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Optionals may not appear as part of a list or array
|
|
||||||
template<typename T, typename... Rest>
|
|
||||||
struct config_io<std::variant<std::optional<T>, Rest...>> {
|
|
||||||
static_assert(!sizeof(std::optional<T>), "Invalid type");
|
|
||||||
};
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
struct config_io<std::list<std::optional<T>>> {
|
|
||||||
static_assert(!sizeof(std::optional<T>), "Invalid type");
|
|
||||||
};
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
struct config_io<std::optional<std::optional<T>>> {
|
|
||||||
static_assert(!sizeof(std::optional<T>), "Invalid type");
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
void set(libconfig::Setting& parent,
|
void set(libconfig::Setting& parent,
|
||||||
|
@ -40,21 +40,19 @@ namespace logid::features {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
namespace {
|
template<typename T>
|
||||||
template<typename T>
|
class _featureWrapper : public T {
|
||||||
class FeatureWrapper : public T {
|
friend class DeviceFeature;
|
||||||
friend class DeviceFeature;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
template<typename... Args>
|
template<typename... Args>
|
||||||
explicit FeatureWrapper(Args... args) : T(std::forward<Args>(args)...) {}
|
explicit _featureWrapper(Args... args) : T(std::forward<Args>(args)...) {}
|
||||||
|
|
||||||
template<typename... Args>
|
template<typename... Args>
|
||||||
static std::shared_ptr<T> make(Args... args) {
|
static std::shared_ptr<T> make(Args... args) {
|
||||||
return std::make_shared<FeatureWrapper>(std::forward<Args>(args)...);
|
return std::make_shared<_featureWrapper>(std::forward<Args>(args)...);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
|
||||||
|
|
||||||
class DeviceFeature {
|
class DeviceFeature {
|
||||||
std::weak_ptr<DeviceFeature> _self;
|
std::weak_ptr<DeviceFeature> _self;
|
||||||
@ -68,7 +66,9 @@ namespace logid::features {
|
|||||||
virtual ~DeviceFeature() = default;
|
virtual ~DeviceFeature() = default;
|
||||||
|
|
||||||
DeviceFeature(const DeviceFeature&) = delete;
|
DeviceFeature(const DeviceFeature&) = delete;
|
||||||
|
|
||||||
DeviceFeature(DeviceFeature&&) = delete;
|
DeviceFeature(DeviceFeature&&) = delete;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
explicit DeviceFeature(Device* dev) : _device(dev) {}
|
explicit DeviceFeature(Device* dev) : _device(dev) {}
|
||||||
|
|
||||||
@ -82,7 +82,7 @@ namespace logid::features {
|
|||||||
public:
|
public:
|
||||||
template<typename T, typename... Args>
|
template<typename T, typename... Args>
|
||||||
static std::shared_ptr<T> make(Args... args) {
|
static std::shared_ptr<T> make(Args... args) {
|
||||||
auto feature = FeatureWrapper<T>::make(std::forward<Args>(args)...);
|
auto feature = _featureWrapper<T>::make(std::forward<Args>(args)...);
|
||||||
feature->_self = feature;
|
feature->_self = feature;
|
||||||
|
|
||||||
return feature;
|
return feature;
|
||||||
|
@ -63,7 +63,7 @@ RemapButton::RemapButton(Device* dev) : DeviceFeature(dev),
|
|||||||
if ((action->reprogFlags() & hidpp20::ReprogControls::RawXYDiverted) &&
|
if ((action->reprogFlags() & hidpp20::ReprogControls::RawXYDiverted) &&
|
||||||
(!_reprog_controls->supportsRawXY() ||
|
(!_reprog_controls->supportsRawXY() ||
|
||||||
!(info.additionalFlags & hidpp20::ReprogControls::RawXY)))
|
!(info.additionalFlags & hidpp20::ReprogControls::RawXY)))
|
||||||
logPrintf(WARN, "%s: Cannot divert raw XY movements for CID 0x%02x",
|
logPrintf(WARN, "%s: 'Cannot divert raw XY movements for CID 0x%02x",
|
||||||
_device->name().c_str(), info.controlID);
|
_device->name().c_str(), info.controlID);
|
||||||
|
|
||||||
report.flags |= action->reprogFlags();
|
report.flags |= action->reprogFlags();
|
||||||
@ -220,8 +220,9 @@ void Button::_makeConfig() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Button::press() const {
|
void Button::press() {
|
||||||
std::shared_lock lock(_action_lock);
|
std::shared_lock lock(_action_lock);
|
||||||
|
_first_move = true;
|
||||||
if (_action)
|
if (_action)
|
||||||
_action->press();
|
_action->press();
|
||||||
}
|
}
|
||||||
@ -232,10 +233,12 @@ void Button::release() const {
|
|||||||
_action->release();
|
_action->release();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Button::move(int16_t x, int16_t y) const {
|
void Button::move(int16_t x, int16_t y) {
|
||||||
std::shared_lock lock(_action_lock);
|
std::shared_lock lock(_action_lock);
|
||||||
if (_action)
|
if (_action && !_first_move)
|
||||||
_action->move(x, y);
|
_action->move(x, y);
|
||||||
|
else if (_first_move)
|
||||||
|
_first_move = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Button::pressed() const {
|
bool Button::pressed() const {
|
||||||
|
@ -36,11 +36,11 @@ namespace logid::features {
|
|||||||
Info info, int index, Device* device, ConfigFunction conf_func,
|
Info info, int index, Device* device, ConfigFunction conf_func,
|
||||||
const std::shared_ptr<ipcgull::node>& root, config::Button& config);
|
const std::shared_ptr<ipcgull::node>& root, config::Button& config);
|
||||||
|
|
||||||
void press() const;
|
void press();
|
||||||
|
|
||||||
void release() const;
|
void release() const;
|
||||||
|
|
||||||
void move(int16_t x, int16_t y) const;
|
void move(int16_t x, int16_t y);
|
||||||
|
|
||||||
void setProfile(config::Button& config);
|
void setProfile(config::Button& config);
|
||||||
|
|
||||||
@ -82,6 +82,8 @@ namespace logid::features {
|
|||||||
std::shared_ptr<actions::Action> _action;
|
std::shared_ptr<actions::Action> _action;
|
||||||
const Info _info;
|
const Info _info;
|
||||||
|
|
||||||
|
bool _first_move{};
|
||||||
|
|
||||||
std::weak_ptr<Button> _self;
|
std::weak_ptr<Button> _self;
|
||||||
|
|
||||||
std::shared_ptr<IPC> _ipc_interface;
|
std::shared_ptr<IPC> _ipc_interface;
|
||||||
|
@ -25,11 +25,35 @@ using namespace logid::backend;
|
|||||||
SmartShift::SmartShift(Device* device) : DeviceFeature(device),
|
SmartShift::SmartShift(Device* device) : DeviceFeature(device),
|
||||||
_config(device->activeProfile().smartshift) {
|
_config(device->activeProfile().smartshift) {
|
||||||
try {
|
try {
|
||||||
_smartshift = std::make_shared<hidpp20::SmartShift>(&device->hidpp20());
|
_smartshift = hidpp20::SmartShift::autoVersion(&device->hidpp20());
|
||||||
} catch (hidpp20::UnsupportedFeature& e) {
|
} catch (hidpp20::UnsupportedFeature& e) {
|
||||||
throw UnsupportedFeature();
|
throw UnsupportedFeature();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_torque_support = _smartshift->supportsTorque();
|
||||||
|
_defaults = _smartshift->getDefaults();
|
||||||
|
|
||||||
|
if (_config.get().has_value()) {
|
||||||
|
auto& config = _config.get().value();
|
||||||
|
|
||||||
|
if (config.threshold.has_value()) {
|
||||||
|
auto& threshold = config.threshold.value();
|
||||||
|
|
||||||
|
/* 0 means no change, clip to 1. */
|
||||||
|
if (threshold == 0)
|
||||||
|
threshold = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config.torque.has_value()) {
|
||||||
|
auto& torque = config.torque.value();
|
||||||
|
/* torque is a percentage, clip between 1-100 */
|
||||||
|
if (torque == 0)
|
||||||
|
torque = 1;
|
||||||
|
else if (torque > 100)
|
||||||
|
torque = 100;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
_ipc_interface = _device->ipcNode()->make_interface<IPC>(this);
|
_ipc_interface = _device->ipcNode()->make_interface<IPC>(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -45,6 +69,9 @@ void SmartShift::configure() {
|
|||||||
settings.setAutoDisengage = conf.threshold.has_value();
|
settings.setAutoDisengage = conf.threshold.has_value();
|
||||||
if (settings.setAutoDisengage)
|
if (settings.setAutoDisengage)
|
||||||
settings.autoDisengage = conf.threshold.value();
|
settings.autoDisengage = conf.threshold.value();
|
||||||
|
settings.setTorque = conf.torque.has_value();
|
||||||
|
if (settings.setTorque)
|
||||||
|
settings.torque = conf.torque.value();
|
||||||
|
|
||||||
_smartshift->setStatus(settings);
|
_smartshift->setStatus(settings);
|
||||||
}
|
}
|
||||||
@ -66,86 +93,108 @@ void SmartShift::setStatus(Status status) {
|
|||||||
_smartshift->setStatus(status);
|
_smartshift->setStatus(status);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const hidpp20::SmartShift::Defaults& SmartShift::getDefaults() const {
|
||||||
|
return _defaults;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SmartShift::supportsTorque() const {
|
||||||
|
return _torque_support;
|
||||||
|
}
|
||||||
|
|
||||||
SmartShift::IPC::IPC(SmartShift* parent) :
|
SmartShift::IPC::IPC(SmartShift* parent) :
|
||||||
ipcgull::interface(
|
ipcgull::interface(
|
||||||
SERVICE_ROOT_NAME ".SmartShift", {
|
SERVICE_ROOT_NAME ".SmartShift", {
|
||||||
{"GetStatus", {this, &IPC::getStatus, {"active", "threshold"}}},
|
{"GetConfig", {this, &IPC::getConfig, {"active", "threshold", "torque"}}},
|
||||||
{"SetActive", {this, &IPC::setActive, {"active"}}},
|
{"SetActive", {this, &IPC::setActive, {"active", "clear"}}},
|
||||||
{"SetThreshold", {this, &IPC::setThreshold, {"threshold"}}},
|
{"SetThreshold", {this, &IPC::setThreshold, {"threshold", "clear"}}},
|
||||||
{"GetDefault", {this, &IPC::getDefault, {"setActive", "active", "setThreshold", "threshold"}}},
|
{"SetTorque", {this, &IPC::setTorque, {"torque", "clear"}}},
|
||||||
{"ClearDefaultActive", {this, &IPC::clearDefaultActive}},
|
},
|
||||||
{"SetDefaultActive", {this, &IPC::setDefaultActive, {"active"}}},
|
{
|
||||||
{"ClearDefaultThreshold", {this, &IPC::clearDefaultThreshold}},
|
{"TorqueSupport", ipcgull::property<bool>(
|
||||||
{"SetDefaultThreshold", {this, &IPC::setDefaultThreshold, {"threshold"}}}
|
ipcgull::property_readable, parent->supportsTorque())},
|
||||||
}, {}, {}),
|
}, {}),
|
||||||
_parent(*parent) {
|
_parent(*parent) {
|
||||||
}
|
}
|
||||||
|
|
||||||
std::tuple<bool, uint8_t> SmartShift::IPC::getStatus() const {
|
std::tuple<uint8_t, uint8_t, uint8_t> SmartShift::IPC::getConfig() const {
|
||||||
auto ret = _parent.getStatus();
|
std::shared_lock lock(_parent._config_mutex);
|
||||||
return std::make_tuple(ret.active, ret.autoDisengage);
|
auto& config = _parent._config.get();
|
||||||
|
auto& defaults = _parent.getDefaults();
|
||||||
|
if (config.has_value()) {
|
||||||
|
auto& conf_value = config.value();
|
||||||
|
return {
|
||||||
|
conf_value.on.has_value() ? (conf_value.on.value() ? 2 : 1) : 0,
|
||||||
|
conf_value.threshold.value_or(defaults.autoDisengage),
|
||||||
|
conf_value.torque.value_or(defaults.torque),
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
return {0, 0, 0};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void SmartShift::IPC::setActive(bool active) {
|
void SmartShift::IPC::setActive(bool active, bool clear) {
|
||||||
Status status{};
|
std::unique_lock lock(_parent._config_mutex);
|
||||||
status.setActive = true;
|
auto& config = _parent._config.get();
|
||||||
status.active = active;
|
if (clear) {
|
||||||
_parent.setStatus(status);
|
if (config.has_value())
|
||||||
|
config.value().on.reset();
|
||||||
|
} else {
|
||||||
|
if (!config.has_value())
|
||||||
|
config = config::SmartShift{};
|
||||||
|
config.value().on = active;
|
||||||
|
Status status{};
|
||||||
|
status.active = active, status.setActive = true;
|
||||||
|
_parent.setStatus(status);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void SmartShift::IPC::setThreshold(uint8_t threshold) {
|
void SmartShift::IPC::setThreshold(uint8_t threshold, bool clear) {
|
||||||
|
std::unique_lock lock(_parent._config_mutex);
|
||||||
|
auto& config = _parent._config.get();
|
||||||
Status status{};
|
Status status{};
|
||||||
status.setAutoDisengage = true;
|
status.setAutoDisengage = true;
|
||||||
status.autoDisengage = threshold;
|
|
||||||
|
/* clip threshold */
|
||||||
|
if (threshold == 0)
|
||||||
|
threshold = 1;
|
||||||
|
|
||||||
|
if (clear) {
|
||||||
|
if (config.has_value())
|
||||||
|
config.value().threshold.reset();
|
||||||
|
status.autoDisengage = _parent.getDefaults().autoDisengage;
|
||||||
|
} else {
|
||||||
|
if (!config.has_value())
|
||||||
|
config = config::SmartShift{};
|
||||||
|
config.value().threshold = threshold;
|
||||||
|
status.autoDisengage = threshold;
|
||||||
|
}
|
||||||
_parent.setStatus(status);
|
_parent.setStatus(status);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::tuple<bool, bool, bool, uint8_t> SmartShift::IPC::getDefault() const {
|
void SmartShift::IPC::setTorque(uint8_t torque, bool clear) {
|
||||||
std::shared_lock lock(_parent._config_mutex);
|
|
||||||
|
|
||||||
auto& config = _parent._config.get();
|
|
||||||
|
|
||||||
if (!config.has_value())
|
|
||||||
return {false, false, false, 0};
|
|
||||||
|
|
||||||
std::tuple<bool, bool, bool, uint8_t> ret;
|
|
||||||
std::get<0>(ret) = config.value().on.has_value();
|
|
||||||
if (std::get<0>(ret))
|
|
||||||
std::get<1>(ret) = config.value().on.value();
|
|
||||||
std::get<2>(ret) = config.value().threshold.has_value();
|
|
||||||
if (std::get<2>(ret))
|
|
||||||
std::get<3>(ret) = config.value().threshold.value();
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
void SmartShift::IPC::clearDefaultActive() {
|
|
||||||
std::unique_lock lock(_parent._config_mutex);
|
std::unique_lock lock(_parent._config_mutex);
|
||||||
auto& config = _parent._config.get();
|
auto& config = _parent._config.get();
|
||||||
if (config.has_value())
|
Status status{};
|
||||||
config.value().on.reset();
|
status.setTorque = true;
|
||||||
}
|
|
||||||
|
|
||||||
void SmartShift::IPC::setDefaultActive(bool active) {
|
/* clip torque */
|
||||||
std::unique_lock lock(_parent._config_mutex);
|
if (torque == 0)
|
||||||
auto& config = _parent._config.get();
|
torque = 1;
|
||||||
if (!config.has_value())
|
else if (torque > 100)
|
||||||
config = config::SmartShift{};
|
torque = 100;
|
||||||
config.value().on = active;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
if (!_parent.supportsTorque())
|
||||||
|
throw std::invalid_argument("torque unsupported");
|
||||||
|
|
||||||
void SmartShift::IPC::clearDefaultThreshold() {
|
if (clear) {
|
||||||
std::unique_lock lock(_parent._config_mutex);
|
if (config.has_value())
|
||||||
auto& config = _parent._config.get();
|
config.value().torque.reset();
|
||||||
if (config.has_value())
|
status.torque = _parent.getDefaults().torque;
|
||||||
config.value().threshold.reset();
|
} else {
|
||||||
}
|
if (!config.has_value())
|
||||||
|
config = config::SmartShift{};
|
||||||
void SmartShift::IPC::setDefaultThreshold(uint8_t threshold) {
|
config.value().torque = torque;
|
||||||
std::unique_lock lock(_parent._config_mutex);
|
status.torque = torque;
|
||||||
auto& config = _parent._config.get();
|
}
|
||||||
if (!config.has_value())
|
_parent.setStatus(status);
|
||||||
config = config::SmartShift{};
|
|
||||||
config.value().threshold = threshold;
|
|
||||||
}
|
}
|
||||||
|
@ -27,19 +27,22 @@
|
|||||||
namespace logid::features {
|
namespace logid::features {
|
||||||
class SmartShift : public DeviceFeature {
|
class SmartShift : public DeviceFeature {
|
||||||
public:
|
public:
|
||||||
|
|
||||||
void configure() final;
|
void configure() final;
|
||||||
|
|
||||||
void listen() final;
|
void listen() final;
|
||||||
|
|
||||||
void setProfile(config::Profile& profile) final;
|
void setProfile(config::Profile& profile) final;
|
||||||
|
|
||||||
typedef backend::hidpp20::SmartShift::SmartshiftStatus Status;
|
typedef backend::hidpp20::SmartShift::Status Status;
|
||||||
|
|
||||||
[[nodiscard]] Status getStatus() const;
|
[[nodiscard]] Status getStatus() const;
|
||||||
|
|
||||||
void setStatus(Status status);
|
void setStatus(Status status);
|
||||||
|
|
||||||
|
[[nodiscard]] const backend::hidpp20::SmartShift::Defaults& getDefaults() const;
|
||||||
|
|
||||||
|
[[nodiscard]] bool supportsTorque() const;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
explicit SmartShift(Device* dev);
|
explicit SmartShift(Device* dev);
|
||||||
|
|
||||||
@ -48,25 +51,20 @@ namespace logid::features {
|
|||||||
std::reference_wrapper<std::optional<config::SmartShift>> _config;
|
std::reference_wrapper<std::optional<config::SmartShift>> _config;
|
||||||
std::shared_ptr<backend::hidpp20::SmartShift> _smartshift;
|
std::shared_ptr<backend::hidpp20::SmartShift> _smartshift;
|
||||||
|
|
||||||
|
backend::hidpp20::SmartShift::Defaults _defaults{};
|
||||||
|
bool _torque_support = false;
|
||||||
|
|
||||||
class IPC : public ipcgull::interface {
|
class IPC : public ipcgull::interface {
|
||||||
public:
|
public:
|
||||||
explicit IPC(SmartShift* parent);
|
explicit IPC(SmartShift* parent);
|
||||||
|
|
||||||
[[nodiscard]] std::tuple<bool, uint8_t> getStatus() const;;
|
[[nodiscard]] std::tuple<uint8_t, uint8_t, uint8_t> getConfig() const;
|
||||||
|
|
||||||
void setActive(bool active);
|
void setActive(bool active, bool clear);
|
||||||
|
|
||||||
void setThreshold(uint8_t threshold);
|
void setThreshold(uint8_t threshold, bool clear);
|
||||||
|
|
||||||
[[nodiscard]] std::tuple<bool, bool, bool, uint8_t> getDefault() const;
|
void setTorque(uint8_t torque, bool clear);
|
||||||
|
|
||||||
void clearDefaultActive();
|
|
||||||
|
|
||||||
void setDefaultActive(bool active);
|
|
||||||
|
|
||||||
void clearDefaultThreshold();
|
|
||||||
|
|
||||||
void setDefaultThreshold(uint8_t threshold);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
SmartShift& _parent;
|
SmartShift& _parent;
|
||||||
|
@ -135,12 +135,18 @@ int main(int argc, char** argv) {
|
|||||||
std::shared_ptr<Configuration> config;
|
std::shared_ptr<Configuration> config;
|
||||||
std::shared_ptr<InputDevice> virtual_input;
|
std::shared_ptr<InputDevice> virtual_input;
|
||||||
|
|
||||||
|
|
||||||
|
/* Set stdout buff to Null so that loging system like journal
|
||||||
|
* can actually read it.
|
||||||
|
*/
|
||||||
|
setbuf(stdout, NULL);
|
||||||
|
|
||||||
// Read config
|
// Read config
|
||||||
try {
|
try {
|
||||||
config = std::make_shared<Configuration>(options.config_file);
|
config = std::make_shared<Configuration>(options.config_file);
|
||||||
}
|
} catch (std::exception &e) {
|
||||||
catch (std::exception& e) {
|
logPrintf(ERROR, "%s", e.what());
|
||||||
config = std::make_shared<Configuration>();
|
return EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
init_workers(config->workers.value_or(defaults::workers));
|
init_workers(config->workers.value_or(defaults::workers));
|
||||||
|
@ -8,7 +8,6 @@ Wants=multi-user.target
|
|||||||
Type=simple
|
Type=simple
|
||||||
ExecStart=${CMAKE_INSTALL_PREFIX}/bin/logid
|
ExecStart=${CMAKE_INSTALL_PREFIX}/bin/logid
|
||||||
User=root
|
User=root
|
||||||
Restart=on-failure
|
|
||||||
|
|
||||||
[Install]
|
[Install]
|
||||||
WantedBy=graphical.target
|
WantedBy=graphical.target
|
||||||
|
@ -3,11 +3,12 @@
|
|||||||
"http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
|
"http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
|
||||||
|
|
||||||
<busconfig>
|
<busconfig>
|
||||||
<policy user="root">
|
<policy context="default">
|
||||||
<allow own="pizza.pixl.LogiOps"/>
|
<deny receive_sender="pizza.pixl.LogiOps"/>
|
||||||
</policy>
|
</policy>
|
||||||
|
|
||||||
<policy context="default">
|
<policy user="root">
|
||||||
|
<allow own="pizza.pixl.LogiOps"/>
|
||||||
<allow send_destination="pizza.pixl.LogiOps"/>
|
<allow send_destination="pizza.pixl.LogiOps"/>
|
||||||
<allow receive_sender="pizza.pixl.LogiOps"/>
|
<allow receive_sender="pizza.pixl.LogiOps"/>
|
||||||
</policy>
|
</policy>
|
||||||
|
@ -18,6 +18,7 @@
|
|||||||
#include <util/task.h>
|
#include <util/task.h>
|
||||||
#include <queue>
|
#include <queue>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
|
#include <cassert>
|
||||||
|
|
||||||
using namespace logid;
|
using namespace logid;
|
||||||
using namespace std::chrono;
|
using namespace std::chrono;
|
||||||
@ -35,18 +36,38 @@ static std::priority_queue<task, std::vector<task>, task_less> tasks {};
|
|||||||
static std::mutex task_mutex {};
|
static std::mutex task_mutex {};
|
||||||
static std::condition_variable task_cv {};
|
static std::condition_variable task_cv {};
|
||||||
static std::atomic_bool workers_init = false;
|
static std::atomic_bool workers_init = false;
|
||||||
|
static std::atomic_bool workers_run = false;
|
||||||
|
|
||||||
[[noreturn]] static void worker() {
|
void stop_workers() {
|
||||||
std::unique_lock lock(task_mutex);
|
std::unique_lock lock(task_mutex);
|
||||||
while (true) {
|
if (workers_init) {
|
||||||
task_cv.wait(lock, []() { return !tasks.empty(); });
|
workers_run = false;
|
||||||
|
lock.unlock();
|
||||||
|
task_cv.notify_all();
|
||||||
|
|
||||||
|
/* Wait for all workers to end */
|
||||||
|
lock.lock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void worker() {
|
||||||
|
std::unique_lock lock(task_mutex);
|
||||||
|
while (workers_run) {
|
||||||
|
task_cv.wait(lock, []() { return !tasks.empty() || !workers_run; });
|
||||||
|
|
||||||
|
if (!workers_run)
|
||||||
|
break;
|
||||||
|
|
||||||
/* top task is in the future, wait */
|
/* top task is in the future, wait */
|
||||||
if (tasks.top().time >= system_clock::now()) {
|
if (tasks.top().time >= system_clock::now()) {
|
||||||
auto wait = tasks.top().time - system_clock::now();
|
auto wait = tasks.top().time - system_clock::now();
|
||||||
task_cv.wait_for(lock, wait, []() {
|
task_cv.wait_for(lock, wait, []() {
|
||||||
return !tasks.empty() && (tasks.top().time < system_clock::now());
|
return (!tasks.empty() && (tasks.top().time < system_clock::now())) ||
|
||||||
|
!workers_run;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (!workers_run)
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!tasks.empty()) {
|
if (!tasks.empty()) {
|
||||||
@ -67,11 +88,15 @@ static std::atomic_bool workers_init = false;
|
|||||||
|
|
||||||
void logid::init_workers(int worker_count) {
|
void logid::init_workers(int worker_count) {
|
||||||
std::lock_guard lock(task_mutex);
|
std::lock_guard lock(task_mutex);
|
||||||
|
assert(!workers_init);
|
||||||
|
|
||||||
for (int i = 0; i < worker_count; ++i)
|
for (int i = 0; i < worker_count; ++i)
|
||||||
std::thread(&worker).detach();
|
std::thread(&worker).detach();
|
||||||
|
|
||||||
workers_init = true;
|
workers_init = true;
|
||||||
|
workers_run = true;
|
||||||
|
|
||||||
|
atexit(&stop_workers);
|
||||||
}
|
}
|
||||||
|
|
||||||
void logid::run_task(std::function<void()> function) {
|
void logid::run_task(std::function<void()> function) {
|
||||||
|
@ -1 +0,0 @@
|
|||||||
v0.3.0
|
|
Loading…
Reference in New Issue
Block a user