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"]
|
||||
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)
|
||||
|
||||
@ -6,6 +6,7 @@ project(logiops)
|
||||
|
||||
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
||||
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_STANDARD 20)
|
||||
@ -13,13 +14,14 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
|
||||
option(USE_USER_BUS "Uses user bus" OFF)
|
||||
|
||||
find_package(Git REQUIRED)
|
||||
find_package(Git)
|
||||
|
||||
# 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
|
||||
OUTPUT_VARIABLE LOGIOPS_VERSION
|
||||
RESULT_VARIABLE LOGIOPS_VERSION_RET
|
||||
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
|
||||
ERROR_QUIET)
|
||||
if(NOT LOGIOPS_VERSION_RET EQUAL "0")
|
||||
execute_process(COMMAND ${GIT_EXECUTABLE}
|
||||
@ -29,16 +31,18 @@ if(EXISTS ${CMAKE_SOURCE_DIR}/.git)
|
||||
endif()
|
||||
|
||||
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})
|
||||
elseif(EXISTS ${CMAKE_SOURCE_DIR}/version.txt)
|
||||
elseif(EXISTS ${PROJECT_SOURCE_DIR}/version.txt)
|
||||
file(READ version.txt LOGIOPS_VERSION)
|
||||
string(REGEX REPLACE "\n$" "" LOGIOPS_VERSION ${LOGIOPS_VERSION})
|
||||
|
||||
IF(NOT EXISTS src/ipcgull)
|
||||
endif()
|
||||
|
||||
IF(NOT EXISTS ${PROJECT_SOURCE_DIR}/src/ipcgull)
|
||||
message(FATAL_ERROR "Missing ipcgull submodule")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
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.
|
||||
|
||||
@ -13,20 +15,20 @@ Default location for the configuration file is /etc/logid.cfg, but another can b
|
||||
|
||||
## 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.
|
||||
|
||||
**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`
|
||||
|
||||
**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
|
||||
|
||||
|
@ -2,15 +2,19 @@
|
||||
|
||||
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 |
|
||||
| :------------: | :---------: | :------------------------------------: |
|
||||
| :-----------------: | :---------: | :------------------------------------: |
|
||||
| MX Master 3S | Yes | `MX Master 3S` |
|
||||
| MX Master 3 | Yes | `Wireless Mouse MX Master 3` |
|
||||
| MX Master 3 for Mac | Yes | `MX Master 3 for Mac` |
|
||||
| MX Master 2S | Yes | `Wireless Mouse MX Master 2S` |
|
||||
| MX Master | Yes | `Wireless Mouse MX Master` |
|
||||
| MX Anywhere S2 | Yes | `Wireless Mobile Mouse MX Anywhere 2S` |
|
||||
| MX Anywhere 3 | Yes | `MX Anywhere 3` |
|
||||
| MX Vertical | Yes | `MX Vertical Advanced Ergonomic Mouse` |
|
||||
| MX Ergo | Yes | `MX Ergo Multi-Device Trackball ` |
|
||||
| MX Ergo M575 | Yes | `ERGO M575 Trackball` |
|
||||
| M720 | Yes | `M720 Triathlon Multi-Device Mouse` |
|
||||
| M590 | Yes | `M585/M590 Multi-Device Mouse` |
|
||||
| T400 | Yes | `Zone Touch Mouse T400` |
|
||||
|
@ -5,6 +5,7 @@ devices: (
|
||||
{
|
||||
on: true;
|
||||
threshold: 30;
|
||||
torque: 50;
|
||||
};
|
||||
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)
|
||||
|
||||
# C++20 is only needed for string literal template parameters
|
||||
set(CMAKE_CXX_STANDARD 20)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
|
||||
set(CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/../CMake")
|
||||
|
||||
@ -12,7 +13,7 @@ find_package(PkgConfig REQUIRED)
|
||||
add_executable(logid
|
||||
logid.cpp
|
||||
util/log.cpp
|
||||
config/util.cpp
|
||||
config/config.cpp
|
||||
InputDevice.cpp
|
||||
DeviceManager.cpp
|
||||
Device.cpp
|
||||
@ -71,7 +72,6 @@ add_executable(logid
|
||||
set_target_properties(logid PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR})
|
||||
|
||||
pkg_check_modules(PC_EVDEV libevdev REQUIRED)
|
||||
pkg_check_modules(DBUS "dbus-1")
|
||||
pkg_check_modules(SYSTEMD "systemd")
|
||||
pkg_check_modules(LIBCONFIG libconfig 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.")
|
||||
endif ()
|
||||
|
||||
if(DBUS_FOUND)
|
||||
# Install DBus conf
|
||||
# TODO: Is there a better way of setting the system policy directory?
|
||||
set(DBUS_SYSTEM_POLICY_INSTALL_DIR "/usr/share/dbus-1/system.d")
|
||||
configure_file(logiops-dbus.conf.in ${CMAKE_BINARY_DIR}/pizza.pixl.LogiOps.conf)
|
||||
message(STATUS "dbus system policy will be installed at ${DBUS_SYSTEM_POLICY_INSTALL_DIR}")
|
||||
install(FILES ${CMAKE_BINARY_DIR}/pizza.pixl.LogiOps.conf
|
||||
# Install DBus conf
|
||||
# TODO: Is there a better way of setting the system policy directory?
|
||||
set(DBUS_SYSTEM_POLICY_INSTALL_DIR "/usr/share/dbus-1/system.d")
|
||||
configure_file(logiops-dbus.conf.in ${CMAKE_BINARY_DIR}/pizza.pixl.LogiOps.conf)
|
||||
message(STATUS "dbus system policy will be installed at ${DBUS_SYSTEM_POLICY_INSTALL_DIR}")
|
||||
install(FILES ${CMAKE_BINARY_DIR}/pizza.pixl.LogiOps.conf
|
||||
DESTINATION ${DBUS_SYSTEM_POLICY_INSTALL_DIR}
|
||||
COMPONENT cp)
|
||||
endif()
|
@ -19,6 +19,7 @@
|
||||
#include <Configuration.h>
|
||||
#include <util/log.h>
|
||||
#include <utility>
|
||||
#include <filesystem>
|
||||
#include <ipc_defs.h>
|
||||
|
||||
using namespace logid;
|
||||
@ -27,8 +28,9 @@ using namespace logid::config;
|
||||
|
||||
Configuration::Configuration(std::string config_file) :
|
||||
_config_file(std::move(config_file)) {
|
||||
if (std::filesystem::exists(_config_file)) {
|
||||
try {
|
||||
_config.readFile(_config_file);
|
||||
_config.readFile(_config_file.c_str());
|
||||
} catch (const FileIOException& e) {
|
||||
logPrintf(ERROR, "I/O Error while reading %s: %s", _config_file.c_str(),
|
||||
e.what());
|
||||
@ -40,6 +42,9 @@ Configuration::Configuration(std::string config_file) :
|
||||
}
|
||||
|
||||
Config::operator=(get<Config>(_config.getRoot()));
|
||||
} else {
|
||||
logPrintf(INFO, "Config file does not exist, using empty config.");
|
||||
}
|
||||
|
||||
if (!devices.has_value())
|
||||
devices.emplace();
|
||||
@ -52,7 +57,7 @@ Configuration::Configuration() {
|
||||
void Configuration::save() {
|
||||
config::set(_config.getRoot(), *this);
|
||||
try {
|
||||
_config.writeFile(_config_file);
|
||||
_config.writeFile(_config_file.c_str());
|
||||
} catch (const FileIOException& e) {
|
||||
logPrintf(ERROR, "I/O Error while writing %s: %s",
|
||||
_config_file.c_str(), e.what());
|
||||
|
@ -100,7 +100,6 @@ Device::Device(std::string path, backend::hidpp::DeviceIndex index,
|
||||
_path(std::move(path)), _index(index),
|
||||
_config(_getConfig(manager, _hidpp20->name())),
|
||||
_profile_name(ipcgull::property_readable, ""),
|
||||
_receiver(nullptr),
|
||||
_manager(manager),
|
||||
_nickname(manager),
|
||||
_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),
|
||||
_config(_getConfig(manager, _hidpp20->name())),
|
||||
_profile_name(ipcgull::property_readable, ""),
|
||||
_receiver(nullptr),
|
||||
_manager(manager),
|
||||
_nickname(manager),
|
||||
_ipc_node(manager->devicesNode()->make_child(_nickname)),
|
||||
@ -132,7 +130,6 @@ Device::Device(Receiver* receiver, hidpp::DeviceIndex index,
|
||||
_path(receiver->path()), _index(index),
|
||||
_config(_getConfig(manager, _hidpp20->name())),
|
||||
_profile_name(ipcgull::property_readable, ""),
|
||||
_receiver(receiver),
|
||||
_manager(manager),
|
||||
_nickname(manager),
|
||||
_ipc_node(manager->devicesNode()->make_child(_nickname)),
|
||||
@ -187,7 +184,6 @@ void Device::sleep() {
|
||||
|
||||
void Device::wakeup() {
|
||||
std::lock_guard<std::mutex> lock(_state_lock);
|
||||
logPrintf(INFO, "%s:%d woke up.", _path.c_str(), _index);
|
||||
|
||||
reconfigure();
|
||||
|
||||
@ -195,6 +191,8 @@ void Device::wakeup() {
|
||||
_awake = true;
|
||||
_ipc_interface->notifyStatus();
|
||||
}
|
||||
|
||||
logPrintf(INFO, "%s:%d woke up.", _path.c_str(), _index);
|
||||
}
|
||||
|
||||
void Device::reconfigure() {
|
||||
|
@ -157,7 +157,6 @@ namespace logid {
|
||||
ipcgull::property<std::string> _profile_name;
|
||||
std::map<std::string, config::Profile>::iterator _profile;
|
||||
|
||||
Receiver* _receiver;
|
||||
const std::weak_ptr<DeviceManager> _manager;
|
||||
|
||||
void _makeResetMechanism();
|
||||
|
@ -66,11 +66,11 @@ void DeviceManager::addDevice(std::string path) {
|
||||
|
||||
// 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() &&
|
||||
config()->ignore.value().contains(raw_dev.productId())) {
|
||||
config()->ignore.value().contains(raw_dev->productId())) {
|
||||
logPrintf(DEBUG, "%s: Device 0x%04x ignored.",
|
||||
path.c_str(), raw_dev.productId());
|
||||
path.c_str(), raw_dev->productId());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -21,6 +21,7 @@
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <mutex>
|
||||
|
||||
extern "C"
|
||||
{
|
||||
|
@ -92,6 +92,12 @@ GestureAction::GestureAction(Device* dev, config::GestureAction& config,
|
||||
direction,Gesture::makeGesture(
|
||||
dev, x.second,
|
||||
_node->make_child(fromDirection(direction))));
|
||||
if (direction == None) {
|
||||
auto& gesture = x.second;
|
||||
std::visit([](auto&& x) {
|
||||
x.threshold.emplace(0);
|
||||
}, gesture);
|
||||
}
|
||||
} catch (std::invalid_argument& e) {
|
||||
logPrintf(WARN, "%s is not a direction", x.first.c_str());
|
||||
}
|
||||
@ -122,7 +128,7 @@ void GestureAction::release() {
|
||||
}
|
||||
|
||||
for (auto& gesture: _gestures) {
|
||||
if (gesture.first == d)
|
||||
if (gesture.first == d || gesture.first == None)
|
||||
continue;
|
||||
if (!threshold_met) {
|
||||
if (gesture.second->metThreshold()) {
|
||||
@ -130,21 +136,15 @@ void GestureAction::release() {
|
||||
// secondary one.
|
||||
threshold_met = true;
|
||||
gesture.second->release(true);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
gesture.second->release(false);
|
||||
}
|
||||
}
|
||||
|
||||
if (!threshold_met) {
|
||||
try {
|
||||
auto none = _gestures.at(None);
|
||||
if (none) {
|
||||
none->press(false);
|
||||
none->release(false);
|
||||
}
|
||||
} catch (std::out_of_range& e) {}
|
||||
auto none_gesture = _gestures.find(None);
|
||||
if (none_gesture != _gestures.end()) {
|
||||
none_gesture->second->release(!threshold_met);
|
||||
}
|
||||
}
|
||||
|
||||
@ -239,15 +239,23 @@ void GestureAction::setGesture(const std::string& direction, const std::string&
|
||||
|
||||
auto dir_name = fromDirection(d);
|
||||
|
||||
auto& gesture = _config.gestures.value()[dir_name];
|
||||
|
||||
_gestures[d].reset();
|
||||
try {
|
||||
_gestures[d] = Gesture::makeGesture(
|
||||
_device, type, _config.gestures.value()[dir_name],
|
||||
_device, type, gesture,
|
||||
_node->make_child(dir_name));
|
||||
} catch (InvalidGesture& e) {
|
||||
_gestures[d] = Gesture::makeGesture(
|
||||
_device, _config.gestures.value()[dir_name],
|
||||
_device, gesture,
|
||||
_node->make_child(dir_name));
|
||||
throw std::invalid_argument("Invalid gesture type");
|
||||
}
|
||||
|
||||
if (d == None) {
|
||||
std::visit([](auto&& x) {
|
||||
x.threshold = 0;
|
||||
}, gesture);
|
||||
}
|
||||
}
|
||||
|
@ -47,5 +47,5 @@ bool NullGesture::wheelCompatibility() 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 <shared_mutex>
|
||||
#include <list>
|
||||
#include <atomic>
|
||||
|
||||
template <class T>
|
||||
class EventHandlerLock;
|
||||
|
||||
template <class T>
|
||||
struct EventHandlerList {
|
||||
typedef std::list<typename T::EventHandler> list_t;
|
||||
typedef list_t::const_iterator iterator_t;
|
||||
class EventHandlerList {
|
||||
public:
|
||||
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;
|
||||
mutable std::shared_mutex mutex;
|
||||
void cleanup() {
|
||||
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) {
|
||||
std::unique_lock lock(mutex);
|
||||
std::unique_lock lock(mutex, std::try_to_lock);
|
||||
if (lock.owns_lock()) {
|
||||
std::unique_lock add_lock(add_mutex);
|
||||
list.erase(iterator);
|
||||
} else {
|
||||
iterator->second = false;
|
||||
}
|
||||
}
|
||||
|
||||
template <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) :
|
||||
io_timeout(duration_cast<milliseconds>(
|
||||
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) {
|
||||
}
|
||||
|
||||
@ -114,35 +114,8 @@ void Device::_setupReportsAndInit() {
|
||||
/* hid_logitech_dj creates virtual /dev/hidraw nodes for receiver
|
||||
* devices. We should ignore these devices.
|
||||
*/
|
||||
if (_index == hidpp::DefaultDevice) {
|
||||
_raw_handler = _raw_device->addEventHandler(
|
||||
{[](const std::vector<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)
|
||||
if (_raw_device->isSubDevice())
|
||||
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(
|
||||
{[index = _index](
|
||||
@ -214,25 +187,21 @@ void Device::_init() {
|
||||
}
|
||||
|
||||
EventHandlerLock<Device> Device::addEventHandler(EventHandler handler) {
|
||||
std::unique_lock lock(_event_handlers->mutex);
|
||||
_event_handlers->list.emplace_front(std::move(handler));
|
||||
return {_event_handlers, _event_handlers->list.cbegin()};
|
||||
return {_event_handlers, _event_handlers->add(std::move(handler))};
|
||||
}
|
||||
|
||||
void Device::handleEvent(Report& report) {
|
||||
if (responseReport(report))
|
||||
return;
|
||||
|
||||
std::shared_lock lock(_event_handlers->mutex);
|
||||
for (auto& handler: _event_handlers->list)
|
||||
if (handler.condition(report))
|
||||
handler.callback(report);
|
||||
_event_handlers->run_all(report);
|
||||
}
|
||||
|
||||
Report Device::sendReport(const Report& report) {
|
||||
/* Must complete transaction before next send */
|
||||
std::lock_guard send_lock(_send_mutex);
|
||||
_sent_sub_id = report.subId();
|
||||
_sent_address = report.address();
|
||||
std::unique_lock lock(_response_mutex);
|
||||
_sendReport(report);
|
||||
|
||||
@ -249,6 +218,7 @@ Report Device::sendReport(const Report& report) {
|
||||
Response response = _response.value();
|
||||
_response.reset();
|
||||
_sent_sub_id.reset();
|
||||
_sent_address.reset();
|
||||
|
||||
if (std::holds_alternative<Report>(response)) {
|
||||
return std::get<Report>(response);
|
||||
@ -268,20 +238,23 @@ bool Device::responseReport(const Report& report) {
|
||||
std::lock_guard lock(_response_mutex);
|
||||
Response response = report;
|
||||
uint8_t sub_id;
|
||||
uint8_t address;
|
||||
|
||||
Report::Hidpp10Error hidpp10_error{};
|
||||
Report::Hidpp20Error hidpp20_error{};
|
||||
if (report.isError10(hidpp10_error)) {
|
||||
sub_id = hidpp10_error.sub_id;
|
||||
address = hidpp10_error.address;
|
||||
response = hidpp10_error;
|
||||
} else if (report.isError20(hidpp20_error)) {
|
||||
sub_id = hidpp20_error.feature_index;
|
||||
response = hidpp20_error;
|
||||
address = (hidpp20_error.function << 4) | (hidpp20_error.software_id & 0x0f);
|
||||
} else {
|
||||
sub_id = report.subId();
|
||||
address = report.address();
|
||||
}
|
||||
|
||||
if (sub_id == _sent_sub_id) {
|
||||
if (sub_id == _sent_sub_id && address == _sent_address) {
|
||||
_response = response;
|
||||
_response_cv.notify_all();
|
||||
return true;
|
||||
@ -310,7 +283,7 @@ bool Device::isStable10() {
|
||||
}
|
||||
|
||||
bool Device::isStable20() {
|
||||
static constexpr std::string ping_seq = "hello";
|
||||
static const std::string ping_seq = "hello";
|
||||
|
||||
hidpp20::Root root(this);
|
||||
|
||||
|
@ -38,24 +38,25 @@ namespace logid::backend::hidpp10 {
|
||||
namespace logid::backend::hidpp {
|
||||
struct DeviceConnectionEvent;
|
||||
|
||||
namespace {
|
||||
template <typename T>
|
||||
class DeviceWrapper : public T {
|
||||
template<typename T>
|
||||
class _deviceWrapper : public T {
|
||||
friend class Device;
|
||||
public:
|
||||
template <typename... Args>
|
||||
explicit DeviceWrapper(Args... args) : T(std::forward<Args>(args)...) { }
|
||||
|
||||
template <typename... Args>
|
||||
public:
|
||||
template<typename... 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)...);
|
||||
return std::make_shared<_deviceWrapper>(std::forward<Args>(args)...);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
class Device {
|
||||
template <typename T>
|
||||
friend class DeviceWrapper;
|
||||
template<typename T>
|
||||
friend
|
||||
class _deviceWrapper;
|
||||
|
||||
public:
|
||||
struct EventHandler {
|
||||
std::function<bool(Report&)> condition;
|
||||
@ -102,7 +103,9 @@ namespace logid::backend::hidpp {
|
||||
[[nodiscard]] const std::shared_ptr<raw::RawDevice>& rawDevice() const;
|
||||
|
||||
Device(const Device&) = delete;
|
||||
|
||||
Device(Device&&) = delete;
|
||||
|
||||
virtual ~Device() = default;
|
||||
|
||||
protected:
|
||||
@ -155,22 +158,23 @@ namespace logid::backend::hidpp {
|
||||
|
||||
std::optional<Response> _response;
|
||||
std::optional<uint8_t> _sent_sub_id{};
|
||||
std::optional<uint8_t> _sent_address{};
|
||||
|
||||
std::shared_ptr<EventHandlerList<Device>> _event_handlers;
|
||||
|
||||
std::weak_ptr<Device> _self;
|
||||
|
||||
protected:
|
||||
template <typename T, typename... Args>
|
||||
template<typename T, typename... 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->_setupReportsAndInit();
|
||||
return device;
|
||||
}
|
||||
|
||||
public:
|
||||
template <typename... Args>
|
||||
template<typename... Args>
|
||||
static std::shared_ptr<Device> make(Args... args) {
|
||||
return makeDerived<Device>(std::forward<Args>(args)...);
|
||||
}
|
||||
|
@ -42,7 +42,7 @@ namespace logid::backend::hidpp {
|
||||
|
||||
static constexpr uint8_t softwareID = 2;
|
||||
/* For sending reports with no response, use a different SW ID */
|
||||
static constexpr uint8_t noAckSoftwareID = 1;
|
||||
static constexpr uint8_t noAckSoftwareID = 3;
|
||||
|
||||
static constexpr std::size_t ShortParamLength = 3;
|
||||
static constexpr std::size_t LongParamLength = 16;
|
||||
|
@ -24,6 +24,23 @@
|
||||
using namespace logid::backend;
|
||||
using namespace logid::backend::hidpp10;
|
||||
|
||||
hidpp::Report setupRegReport(hidpp::DeviceIndex index,
|
||||
uint8_t sub_id, uint8_t address,
|
||||
const std::vector<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,
|
||||
const std::shared_ptr<raw::DeviceMonitor>& monitor, double 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);
|
||||
}
|
||||
|
||||
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,
|
||||
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, deviceIndex(), sub_id, address);
|
||||
std::copy(params.begin(), params.end(), request.paramBegin());
|
||||
|
||||
auto response = sendReport(request);
|
||||
auto response = sendReport(setupRegReport(deviceIndex(), sub_id, address, params));
|
||||
return {response.paramBegin(), response.paramEnd()};
|
||||
}
|
||||
|
||||
|
||||
|
||||
void Device::accessRegisterNoResponse(uint8_t sub_id, uint8_t address,
|
||||
const std::vector<uint8_t>& params) {
|
||||
sendReportNoACK(setupRegReport(deviceIndex(), sub_id, address, params));
|
||||
}
|
||||
|
@ -39,6 +39,9 @@ namespace logid::backend::hidpp10 {
|
||||
const std::vector<uint8_t>& params,
|
||||
hidpp::Report::Type type);
|
||||
|
||||
void setRegisterNoResponse(uint8_t address, const std::vector<uint8_t>& params,
|
||||
hidpp::Report::Type type);
|
||||
|
||||
protected:
|
||||
Device(const std::string& path, hidpp::DeviceIndex index,
|
||||
const std::shared_ptr<raw::DeviceMonitor>& monitor, double timeout);
|
||||
@ -53,18 +56,24 @@ namespace logid::backend::hidpp10 {
|
||||
|
||||
private:
|
||||
typedef std::variant<hidpp::Report, hidpp::Report::Hidpp10Error> Response;
|
||||
|
||||
struct ResponseSlot {
|
||||
std::optional<Response> response;
|
||||
std::optional<uint8_t> sub_id;
|
||||
|
||||
void reset();
|
||||
};
|
||||
|
||||
std::array<ResponseSlot, SubIDCount> _responses;
|
||||
|
||||
std::vector<uint8_t> accessRegister(
|
||||
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:
|
||||
template <typename T, typename... Args>
|
||||
template<typename T, typename... Args>
|
||||
static std::shared_ptr<T> makeDerived(Args... args) {
|
||||
auto device = hidpp::Device::makeDerived<T>(std::forward<Args>(args)...);
|
||||
|
||||
@ -73,8 +82,9 @@ namespace logid::backend::hidpp10 {
|
||||
|
||||
return device;
|
||||
}
|
||||
|
||||
public:
|
||||
template <typename... Args>
|
||||
template<typename... Args>
|
||||
static std::shared_ptr<Device> make(Args... args) {
|
||||
return makeDerived<Device>(std::forward<Args>(args)...);
|
||||
}
|
||||
|
@ -68,7 +68,7 @@ void Receiver::setNotifications(NotificationFlags flags) {
|
||||
}
|
||||
|
||||
void Receiver::enumerate() {
|
||||
setRegister(ConnectionState, {2}, hidpp::ReportType::Short);
|
||||
setRegisterNoResponse(ConnectionState, {2}, hidpp::ReportType::Short);
|
||||
}
|
||||
|
||||
///TODO: Investigate usage
|
||||
|
@ -158,13 +158,17 @@ void ReceiverMonitor::enumerate() {
|
||||
}
|
||||
|
||||
void ReceiverMonitor::waitForDevice(hidpp::DeviceIndex index) {
|
||||
auto handler_id = std::make_shared<EventHandlerLock<raw::RawDevice>>();
|
||||
|
||||
*handler_id = _receiver->rawDevice()->addEventHandler(
|
||||
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 {
|
||||
return report[Offset::DeviceIndex] == index;
|
||||
/* 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, handler_id](
|
||||
[self_weak = _self, index](
|
||||
[[maybe_unused]] const std::vector<uint8_t>& report) {
|
||||
hidpp::DeviceConnectionEvent event{};
|
||||
event.withPayload = false;
|
||||
@ -172,14 +176,13 @@ void ReceiverMonitor::waitForDevice(hidpp::DeviceIndex index) {
|
||||
event.index = index;
|
||||
event.fromTimeoutCheck = true;
|
||||
|
||||
run_task([self_weak, event, handler_id]() {
|
||||
*handler_id = {};
|
||||
if (auto self = self_weak.lock()) {
|
||||
run_task([self_weak, event]() {
|
||||
if (auto self = self_weak.lock())
|
||||
self->_addHandler(event);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<Receiver> ReceiverMonitor::receiver() const {
|
||||
@ -217,6 +220,8 @@ void ReceiverMonitor::_addHandler(const hidpp::DeviceConnectionEvent& event, int
|
||||
auto device_path = _receiver->devicePath();
|
||||
try {
|
||||
addDevice(event);
|
||||
const std::lock_guard lock(_wait_mutex);
|
||||
_waiters.erase(event.index);
|
||||
} catch (DeviceNotReady& e) {
|
||||
if (tries == max_tries) {
|
||||
logPrintf(WARN, "Failed to add device %s:%d after %d tries."
|
||||
|
@ -26,20 +26,19 @@
|
||||
|
||||
namespace logid::backend::hidpp10 {
|
||||
|
||||
namespace {
|
||||
template <typename T>
|
||||
class ReceiverMonitorWrapper : public T {
|
||||
template<typename T>
|
||||
class _receiverMonitorWrapper : public T {
|
||||
friend class ReceiverMonitor;
|
||||
public:
|
||||
template <typename... Args>
|
||||
explicit ReceiverMonitorWrapper(Args... args) : T(std::forward<Args>(args)...) { }
|
||||
|
||||
template <typename... Args>
|
||||
public:
|
||||
template<typename... 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)...);
|
||||
return std::make_shared<_receiverMonitorWrapper>(std::forward<Args>(args)...);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
static constexpr int max_tries = 5;
|
||||
static constexpr int ready_backoff = 250;
|
||||
@ -50,7 +49,9 @@ namespace logid::backend::hidpp10 {
|
||||
void enumerate();
|
||||
|
||||
ReceiverMonitor(const ReceiverMonitor&) = delete;
|
||||
|
||||
ReceiverMonitor(ReceiverMonitor&&) = delete;
|
||||
|
||||
protected:
|
||||
ReceiverMonitor(const std::string& path,
|
||||
const std::shared_ptr<raw::DeviceMonitor>& monitor,
|
||||
@ -100,10 +101,13 @@ namespace logid::backend::hidpp10 {
|
||||
|
||||
std::weak_ptr<ReceiverMonitor> _self;
|
||||
|
||||
std::mutex _wait_mutex;
|
||||
std::map<hidpp::DeviceIndex, EventHandlerLock<raw::RawDevice>> _waiters;
|
||||
|
||||
public:
|
||||
template <typename T, typename... Args>
|
||||
template<typename T, typename... 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->_ready();
|
||||
return receiver_monitor;
|
||||
|
@ -74,6 +74,7 @@ namespace logid::backend::hidpp20 {
|
||||
POINTER_AXES_ORIENTATION = 0x2006,
|
||||
VERTICAL_SCROLLING = 0x2100,
|
||||
SMART_SHIFT = 0x2110,
|
||||
SMART_SHIFT_V2 = 0x2111,
|
||||
HIRES_SCROLLING = 0x2120,
|
||||
HIRES_SCROLLING_V2 = 0x2121, // Referred to as Hi-res wheel in cvuchener/hidpp, seems to be V2?
|
||||
LORES_SCROLLING = 0x2130,
|
||||
|
@ -40,7 +40,7 @@ namespace logid::backend::hidpp20 {
|
||||
struct HostInfo {
|
||||
uint8_t hostCount;
|
||||
uint8_t currentHost;
|
||||
[[maybe_unused]] bool enhancedHostSwitch;
|
||||
bool enhancedHostSwitch;
|
||||
};
|
||||
|
||||
HostInfo getHostInfo();
|
||||
|
@ -129,7 +129,7 @@ ReprogControls::ControlInfo ReprogControls::getControlIdInfo(uint16_t cid) {
|
||||
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.
|
||||
(void) cid;
|
||||
(void) info; // Suppress unused warnings
|
||||
@ -173,7 +173,7 @@ ReprogControls::ControlInfo ReprogControlsV4::getControlReporting(uint16_t cid)
|
||||
return info;
|
||||
}
|
||||
|
||||
void ReprogControlsV4::setControlReporting(uint8_t cid, ControlInfo info) {
|
||||
void ReprogControlsV4::setControlReporting(uint16_t cid, ControlInfo info) {
|
||||
std::vector<uint8_t> params(5);
|
||||
params[0] = (cid >> 8) & 0xff;
|
||||
params[1] = cid & 0xff;
|
||||
|
@ -100,7 +100,7 @@ namespace logid::backend::hidpp20 {
|
||||
[[nodiscard]] virtual ControlInfo getControlReporting(uint16_t cid);
|
||||
|
||||
// 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);
|
||||
|
||||
@ -162,7 +162,7 @@ namespace logid::backend::hidpp20 {
|
||||
|
||||
[[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);
|
||||
|
||||
|
@ -19,26 +19,107 @@
|
||||
|
||||
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);
|
||||
SmartshiftStatus status{};
|
||||
|
||||
auto response = callFunction(GetStatus, params);
|
||||
status.active = response[0] - 1;
|
||||
status.autoDisengage = response[1];
|
||||
status.defaultAutoDisengage = response[2];
|
||||
return status;
|
||||
|
||||
return {
|
||||
.active = static_cast<bool>(response[0] - 1),
|
||||
.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);
|
||||
if (status.setActive)
|
||||
params[0] = status.active + 1;
|
||||
if (status.setAutoDisengage)
|
||||
params[1] = status.autoDisengage;
|
||||
if (status.setDefaultAutoDisengage)
|
||||
params[2] = status.defaultAutoDisengage;
|
||||
callFunction(SetStatus, params);
|
||||
}
|
||||
|
||||
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.h>
|
||||
#include <memory>
|
||||
|
||||
namespace logid::backend::hidpp20 {
|
||||
class SmartShift : public Feature {
|
||||
public:
|
||||
static const uint16_t ID = FeatureID::SMART_SHIFT;
|
||||
|
||||
uint16_t getID() final { return ID; }
|
||||
uint16_t getID() override { return ID; }
|
||||
|
||||
enum Function {
|
||||
GetStatus = 0,
|
||||
@ -35,16 +36,54 @@ namespace logid::backend::hidpp20 {
|
||||
|
||||
explicit SmartShift(Device* dev);
|
||||
|
||||
struct SmartshiftStatus {
|
||||
bool active;
|
||||
struct Defaults {
|
||||
uint8_t autoDisengage;
|
||||
uint8_t defaultAutoDisengage;
|
||||
bool setActive, setAutoDisengage, setDefaultAutoDisengage;
|
||||
uint8_t torque;
|
||||
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;
|
||||
|
||||
_io_monitor->add(_fd, {
|
||||
[this]() {
|
||||
struct udev_device* device = udev_monitor_receive_device(_udev_monitor);
|
||||
[self_weak = _self]() {
|
||||
if (auto self = self_weak.lock()) {
|
||||
struct udev_device* device = udev_monitor_receive_device(self->_udev_monitor);
|
||||
std::string action = udev_device_get_action(device);
|
||||
std::string dev_node = udev_device_get_devnode(device);
|
||||
|
||||
if (action == "add")
|
||||
run_task([self_weak = _self, dev_node]() {
|
||||
run_task([self_weak, dev_node]() {
|
||||
if (auto self = self_weak.lock())
|
||||
self->_addHandler(dev_node);
|
||||
});
|
||||
else if (action == "remove")
|
||||
run_task([self_weak = _self, dev_node]() {
|
||||
run_task([self_weak, dev_node]() {
|
||||
if (auto self = self_weak.lock())
|
||||
self->_removeHandler(dev_node);
|
||||
});
|
||||
|
||||
udev_device_unref(device);
|
||||
}
|
||||
},
|
||||
[]() {
|
||||
throw std::runtime_error("udev hangup");
|
||||
@ -135,13 +137,17 @@ void DeviceMonitor::enumerate() {
|
||||
const char* name = udev_list_entry_get_name(udev_enum_entry);
|
||||
|
||||
struct udev_device* device = udev_device_new_from_syspath(_udev_context, name);
|
||||
if (!device)
|
||||
throw std::runtime_error("udev_device_new_from_syspath failed");
|
||||
|
||||
std::string dev_node = udev_device_get_devnode(device);
|
||||
if (device) {
|
||||
const char* dev_node_cstr = udev_device_get_devnode(device);
|
||||
if (dev_node_cstr) {
|
||||
const std::string dev_node {dev_node_cstr};
|
||||
udev_device_unref(device);
|
||||
|
||||
_addHandler(dev_node);
|
||||
} else {
|
||||
udev_device_unref(device);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
udev_enumerate_unref(udev_enum);
|
||||
|
@ -36,21 +36,19 @@ namespace logid::backend::raw {
|
||||
static constexpr int max_tries = 5;
|
||||
static constexpr int ready_backoff = 500;
|
||||
|
||||
namespace {
|
||||
template<typename T>
|
||||
class DeviceMonitorWrapper : public T {
|
||||
class _deviceMonitorWrapper : public T {
|
||||
friend class Device;
|
||||
|
||||
public:
|
||||
template<typename... Args>
|
||||
explicit DeviceMonitorWrapper(Args... args) : T(std::forward<Args>(args)...) {}
|
||||
explicit _deviceMonitorWrapper(Args... args) : T(std::forward<Args>(args)...) {}
|
||||
|
||||
template<typename... 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 {
|
||||
public:
|
||||
@ -62,7 +60,7 @@ namespace logid::backend::raw {
|
||||
|
||||
template<typename T, typename... 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->ready();
|
||||
|
||||
@ -79,7 +77,7 @@ namespace logid::backend::raw {
|
||||
|
||||
virtual void removeDevice(std::string device) = 0;
|
||||
|
||||
template <typename T>
|
||||
template<typename T>
|
||||
[[nodiscard]] std::weak_ptr<T> self() const {
|
||||
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
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@ -16,7 +16,8 @@
|
||||
*
|
||||
*/
|
||||
#include <backend/raw/IOMonitor.h>
|
||||
#include <cassert>
|
||||
#include <util/log.h>
|
||||
#include <optional>
|
||||
|
||||
extern "C"
|
||||
{
|
||||
@ -56,12 +57,7 @@ IOMonitor::IOMonitor() : _epoll_fd(epoll_create1(0)),
|
||||
throw std::system_error(errno, std::generic_category());
|
||||
}
|
||||
|
||||
_fds.emplace(std::piecewise_construct, std::forward_as_tuple(_event_fd),
|
||||
std::forward_as_tuple([]() {}, []() {
|
||||
throw std::runtime_error("event_fd hangup");
|
||||
}, []() {
|
||||
throw std::runtime_error("event_fd error");
|
||||
}));
|
||||
_fds.emplace(_event_fd, nullptr);
|
||||
|
||||
_io_thread = std::make_unique<std::thread>([this]() {
|
||||
_listen();
|
||||
@ -69,120 +65,103 @@ IOMonitor::IOMonitor() : _epoll_fd(epoll_create1(0)),
|
||||
}
|
||||
|
||||
IOMonitor::~IOMonitor() noexcept {
|
||||
std::lock_guard<std::mutex> ctl_lock(_ctl_lock);
|
||||
_stop();
|
||||
|
||||
if (_event_fd >= 0)
|
||||
close(_event_fd);
|
||||
::close(_event_fd);
|
||||
|
||||
if (_epoll_fd >= 0)
|
||||
close(_epoll_fd);
|
||||
::close(_epoll_fd);
|
||||
}
|
||||
|
||||
void IOMonitor::_listen() {
|
||||
std::lock_guard<std::mutex> run_lock(_run_lock);
|
||||
std::unique_lock lock(_run_mutex);
|
||||
std::vector<struct epoll_event> events;
|
||||
|
||||
if (_is_running)
|
||||
throw std::runtime_error("IOMonitor double run");
|
||||
|
||||
_is_running = true;
|
||||
|
||||
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())
|
||||
events.resize(_fds.size());
|
||||
|
||||
int ev_count = ::epoll_wait(_epoll_fd, events.data(), (int) events.size(), -1);
|
||||
for (int i = 0; i < ev_count; ++i) {
|
||||
const auto& handler = _fds.at(events[i].data.fd);
|
||||
std::shared_ptr<IOHandler> handler;
|
||||
|
||||
if (events[i].data.fd == _event_fd) {
|
||||
if (events[i].events & EPOLLIN) {
|
||||
lock.unlock();
|
||||
/* Wait until done yielding */
|
||||
const std::lock_guard yield_lock(_yield_mutex);
|
||||
uint64_t event;
|
||||
while (-1 != ::eventfd_read(_event_fd, &event)) { }
|
||||
lock.lock();
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
handler = _fds.at(events[i].data.fd);
|
||||
} catch (std::out_of_range& e) {
|
||||
continue;
|
||||
}
|
||||
|
||||
lock.unlock();
|
||||
try {
|
||||
if (events[i].events & EPOLLIN)
|
||||
handler.read();
|
||||
handler->read();
|
||||
if (events[i].events & EPOLLHUP)
|
||||
handler.hangup();
|
||||
handler->hangup();
|
||||
if (events[i].events & EPOLLERR)
|
||||
handler.error();
|
||||
handler->error();
|
||||
} catch (std::exception& e) {
|
||||
logPrintf(ERROR, "Unhandled I/O handler error: %s", e.what());
|
||||
}
|
||||
lock.lock();
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void IOMonitor::_stop() noexcept {
|
||||
_interrupt();
|
||||
_is_running = false;
|
||||
_continue();
|
||||
_yield();
|
||||
_io_thread->join();
|
||||
}
|
||||
|
||||
[[maybe_unused]]
|
||||
bool IOMonitor::_running() const {
|
||||
std::unique_lock<std::mutex> run_lock(_run_lock, std::try_to_lock);
|
||||
return !run_lock.owns_lock() || _is_running;
|
||||
std::unique_lock<std::mutex> IOMonitor::_yield() noexcept {
|
||||
/* Prevent listener thread from grabbing lock during yielding */
|
||||
std::unique_lock yield_lock(_yield_mutex);
|
||||
|
||||
std::unique_lock run_lock(_run_mutex, std::try_to_lock);
|
||||
if (!run_lock.owns_lock()) {
|
||||
::eventfd_write(_event_fd, 1);
|
||||
run_lock = std::unique_lock<std::mutex>(_run_mutex);
|
||||
}
|
||||
|
||||
return run_lock;
|
||||
}
|
||||
|
||||
void IOMonitor::add(int fd, IOHandler handler) {
|
||||
std::lock_guard<std::mutex> lock(_ctl_lock);
|
||||
_interrupt();
|
||||
const auto lock = _yield();
|
||||
|
||||
struct epoll_event event{};
|
||||
event.events = EPOLLIN | EPOLLHUP | EPOLLERR;
|
||||
event.data.fd = fd;
|
||||
|
||||
// TODO: EPOLL_CTL_MOD
|
||||
if (_fds.contains(fd)) {
|
||||
_continue();
|
||||
if (!_fds.contains(fd)) {
|
||||
if (::epoll_ctl(_epoll_fd, EPOLL_CTL_ADD, fd, &event))
|
||||
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");
|
||||
}
|
||||
|
||||
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 {
|
||||
std::lock_guard<std::mutex> lock(_ctl_lock);
|
||||
_interrupt();
|
||||
std::lock_guard<std::mutex> io_lock(_io_lock);
|
||||
|
||||
const auto lock = _yield();
|
||||
::epoll_ctl(_epoll_fd, EPOLL_CTL_DEL, fd, nullptr);
|
||||
_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
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@ -21,8 +21,10 @@
|
||||
#include <atomic>
|
||||
#include <functional>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <condition_variable>
|
||||
#include <thread>
|
||||
|
||||
namespace logid::backend::raw {
|
||||
struct IOHandler {
|
||||
@ -39,33 +41,31 @@ namespace logid::backend::raw {
|
||||
public:
|
||||
IOMonitor();
|
||||
|
||||
IOMonitor(IOMonitor&&) = delete;
|
||||
|
||||
IOMonitor(const IOMonitor&) = delete;
|
||||
|
||||
IOMonitor& operator=(IOMonitor&&) = delete;
|
||||
|
||||
IOMonitor& operator=(const IOMonitor&) = delete;
|
||||
|
||||
~IOMonitor() noexcept;
|
||||
|
||||
void add(int fd, IOHandler handler);
|
||||
|
||||
void remove(int fd) noexcept;
|
||||
|
||||
private:
|
||||
void _listen(); // This is a blocking call
|
||||
void _stop() noexcept;
|
||||
|
||||
[[maybe_unused]]
|
||||
[[nodiscard]] bool _running() const;
|
||||
|
||||
void _interrupt() noexcept;
|
||||
|
||||
void _continue() noexcept;
|
||||
std::unique_lock<std::mutex> _yield() noexcept;
|
||||
|
||||
std::unique_ptr<std::thread> _io_thread;
|
||||
|
||||
std::map<int, IOHandler> _fds;
|
||||
std::mutex _io_lock, _ctl_lock;
|
||||
mutable std::mutex _run_lock;
|
||||
std::atomic_bool _is_running;
|
||||
std::mutex _run_mutex;
|
||||
std::mutex _yield_mutex;
|
||||
|
||||
std::atomic_bool _interrupting;
|
||||
std::mutex _interrupt_mutex;
|
||||
std::condition_variable _interrupt_cv;
|
||||
std::map<int, std::shared_ptr<IOHandler>> _fds;
|
||||
std::atomic_bool _is_running;
|
||||
|
||||
const int _epoll_fd;
|
||||
const int _event_fd;
|
||||
|
@ -24,6 +24,7 @@
|
||||
#include <string>
|
||||
#include <system_error>
|
||||
#include <utility>
|
||||
#include <regex>
|
||||
|
||||
extern "C"
|
||||
{
|
||||
@ -32,12 +33,17 @@ extern "C"
|
||||
#include <fcntl.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <linux/hidraw.h>
|
||||
#include <linux/input.h>
|
||||
}
|
||||
|
||||
using namespace logid::backend::raw;
|
||||
using namespace logid::backend;
|
||||
using namespace std::chrono;
|
||||
|
||||
static constexpr int max_write_tries = 8;
|
||||
|
||||
static const std::regex virtual_path_regex(R"~((.*\/)(.*:)([0-9]+))~");
|
||||
|
||||
int get_fd(const std::string& path) {
|
||||
int fd = ::open(path.c_str(), O_RDWR | O_NONBLOCK);
|
||||
if (fd == -1)
|
||||
@ -56,7 +62,37 @@ RawDevice::dev_info get_dev_info(int fd) {
|
||||
"RawDevice HIDIOCGRAWINFO failed");
|
||||
}
|
||||
|
||||
return {dev_info.vendor, dev_info.product};
|
||||
RawDevice::BusType type = RawDevice::OtherBus;
|
||||
|
||||
switch (dev_info.bustype) {
|
||||
case BUS_USB:
|
||||
type = RawDevice::USB;
|
||||
break;
|
||||
case BUS_BLUETOOTH:
|
||||
type = RawDevice::Bluetooth;
|
||||
break;
|
||||
default:;
|
||||
}
|
||||
|
||||
|
||||
return {
|
||||
.vid = dev_info.vendor,
|
||||
.pid = dev_info.product,
|
||||
.bus_type = type
|
||||
};
|
||||
}
|
||||
|
||||
std::string get_phys(int fd) {
|
||||
ssize_t len;
|
||||
char buf[256];
|
||||
if (-1 == (len = ::ioctl(fd, HIDIOCGRAWPHYS(sizeof(buf)), buf))) {
|
||||
int err = errno;
|
||||
::close(fd);
|
||||
throw std::system_error(err, std::system_category(),
|
||||
"RawDevice HIDIOCGRAWPHYS failed");
|
||||
}
|
||||
|
||||
return {buf, static_cast<size_t>(len) - 1};
|
||||
}
|
||||
|
||||
std::string get_name(int fd) {
|
||||
@ -68,7 +104,7 @@ std::string get_name(int fd) {
|
||||
throw std::system_error(err, std::system_category(),
|
||||
"RawDevice HIDIOCGRAWNAME failed");
|
||||
}
|
||||
return {name_buf, static_cast<size_t>(len)};
|
||||
return {name_buf, static_cast<size_t>(len) - 1};
|
||||
}
|
||||
|
||||
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)),
|
||||
_report_desc(getReportDescriptor(_fd)), _io_monitor(monitor->ioMonitor()),
|
||||
_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, {
|
||||
[this]() { _readReports(); },
|
||||
[this]() { _valid = false; },
|
||||
[this]() { _valid = false; }
|
||||
[self_weak = _self]() {
|
||||
if (auto self = self_weak.lock())
|
||||
self->_readReports();
|
||||
},
|
||||
[self_weak = _self]() {
|
||||
if (auto self = self_weak.lock())
|
||||
self->_valid = false;
|
||||
},
|
||||
[self_weak = _self]() {
|
||||
if (auto self = self_weak.lock())
|
||||
self->_valid = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@ -105,6 +158,14 @@ int16_t RawDevice::productId() const {
|
||||
return _dev_info.pid;
|
||||
}
|
||||
|
||||
RawDevice::BusType RawDevice::busType() const {
|
||||
return _dev_info.bus_type;
|
||||
}
|
||||
|
||||
bool RawDevice::isSubDevice() const {
|
||||
return _sub_device;
|
||||
}
|
||||
|
||||
std::vector<uint8_t> RawDevice::getReportDescriptor(const std::string& path) {
|
||||
int fd = ::open(path.c_str(), O_RDWR | O_NONBLOCK);
|
||||
if (fd == -1)
|
||||
@ -150,15 +211,17 @@ void RawDevice::sendReport(const std::vector<uint8_t>& report) {
|
||||
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) {
|
||||
auto err = errno;
|
||||
if (err != EPIPE)
|
||||
throw std::system_error(err, std::system_category(),
|
||||
"sendReport write failed");
|
||||
}
|
||||
}
|
||||
|
||||
EventHandlerLock<RawDevice> RawDevice::addEventHandler(RawEventHandler handler) {
|
||||
std::unique_lock<std::shared_mutex> lock(_event_handlers->mutex);
|
||||
_event_handlers->list.emplace_front(std::move(handler));
|
||||
return {_event_handlers, _event_handlers->list.cbegin()};
|
||||
return {_event_handlers, _event_handlers->add(std::forward<RawEventHandler>(handler))};
|
||||
}
|
||||
|
||||
void RawDevice::_readReports() {
|
||||
@ -181,8 +244,5 @@ void RawDevice::_readReports() {
|
||||
}
|
||||
|
||||
void RawDevice::_handleEvent(const std::vector<uint8_t>& report) {
|
||||
std::shared_lock<std::shared_mutex> lock(_event_handlers->mutex);
|
||||
for (auto& handler : _event_handlers->list)
|
||||
if (handler.condition(report))
|
||||
handler.callback(report);
|
||||
_event_handlers->run_all(report);
|
||||
}
|
||||
|
@ -34,17 +34,40 @@ namespace logid::backend::raw {
|
||||
|
||||
class IOMonitor;
|
||||
|
||||
template <typename T>
|
||||
class RawDeviceWrapper : public T {
|
||||
public:
|
||||
template <typename... Args>
|
||||
RawDeviceWrapper(Args... args) : T(std::forward<Args>(args)...) { }
|
||||
};
|
||||
|
||||
class RawDevice {
|
||||
template <typename>
|
||||
friend class RawDeviceWrapper;
|
||||
public:
|
||||
static constexpr int max_data_length = 32;
|
||||
typedef RawEventHandler EventHandler;
|
||||
|
||||
enum BusType {
|
||||
USB,
|
||||
Bluetooth,
|
||||
OtherBus
|
||||
};
|
||||
|
||||
struct dev_info {
|
||||
int16_t vid;
|
||||
int16_t pid;
|
||||
BusType bus_type;
|
||||
};
|
||||
|
||||
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;
|
||||
|
||||
@ -57,6 +80,10 @@ namespace logid::backend::raw {
|
||||
|
||||
[[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(int fd);
|
||||
@ -68,6 +95,10 @@ namespace logid::backend::raw {
|
||||
[[nodiscard]] EventHandlerLock<RawDevice> addEventHandler(RawEventHandler handler);
|
||||
|
||||
private:
|
||||
RawDevice(std::string path, const std::shared_ptr<DeviceMonitor>& monitor);
|
||||
|
||||
void _ready();
|
||||
|
||||
void _readReports();
|
||||
|
||||
std::atomic_bool _valid;
|
||||
@ -80,6 +111,10 @@ namespace logid::backend::raw {
|
||||
|
||||
std::shared_ptr<IOMonitor> _io_monitor;
|
||||
|
||||
std::weak_ptr<RawDevice> _self;
|
||||
|
||||
bool _sub_device = false;
|
||||
|
||||
std::shared_ptr<EventHandlerList<RawDevice>> _event_handlers;
|
||||
|
||||
void _handleEvent(const std::vector<uint8_t>& report);
|
||||
|
@ -16,11 +16,15 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include <config/types.h>
|
||||
#include <config/schema.h>
|
||||
#include <util/log.h>
|
||||
|
||||
using namespace logid;
|
||||
|
||||
const char config::keys::name[] = "name";
|
||||
const char config::keys::cid[] = "cid";
|
||||
const char config::keys::direction[] = "direction";
|
||||
|
||||
void config::logError(const libconfig::Setting& setting, std::exception& e) {
|
||||
logPrintf(WARN, "Error at line %d: %s", setting.getSourceLine(), e.what());
|
||||
}
|
@ -22,6 +22,7 @@
|
||||
#include <type_traits>
|
||||
#include <functional>
|
||||
#include <utility>
|
||||
#include <algorithm>
|
||||
|
||||
namespace logid::config {
|
||||
template<typename T>
|
||||
@ -38,30 +39,22 @@ namespace logid::config {
|
||||
template<typename T>
|
||||
void append(libconfig::Setting& list, const T& t);
|
||||
|
||||
namespace {
|
||||
template<typename T, typename... M>
|
||||
struct group_io {
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct group_io<T> {
|
||||
static inline void get(
|
||||
[[maybe_unused]] const libconfig::Setting& s,
|
||||
[[maybe_unused]] T* t,
|
||||
[[maybe_unused]] const std::vector<std::string>& names,
|
||||
[[maybe_unused]] const std::size_t index) {}
|
||||
static void get(const libconfig::Setting&, T*,
|
||||
const std::vector<std::string>&, const std::size_t) {}
|
||||
|
||||
static inline void set(
|
||||
[[maybe_unused]] libconfig::Setting& s,
|
||||
[[maybe_unused]] const T* t,
|
||||
[[maybe_unused]] const std::vector<std::string>& names,
|
||||
[[maybe_unused]] const std::size_t index) {}
|
||||
static void set(libconfig::Setting&, const T*,
|
||||
const std::vector<std::string>&, const std::size_t) {}
|
||||
};
|
||||
|
||||
template<typename T, typename A, typename... M>
|
||||
struct group_io<T, A, M...> {
|
||||
static inline void get(
|
||||
const libconfig::Setting& s, T* t,
|
||||
static void get(const libconfig::Setting& s, T* t,
|
||||
const std::vector<std::string>& names,
|
||||
const std::size_t index, A T::* arg, M T::*... rest) {
|
||||
auto& x = t->*(arg);
|
||||
@ -78,15 +71,13 @@ namespace logid::config {
|
||||
}
|
||||
}
|
||||
|
||||
static inline void set(
|
||||
libconfig::Setting& s, const T* t,
|
||||
static void set(libconfig::Setting& s, const T* t,
|
||||
const std::vector<std::string>& names,
|
||||
const std::size_t index, A T::* arg, M T::*... rest) {
|
||||
config::set(s, names[index], t->*(arg));
|
||||
group_io<T, M...>::set(s, t, names, index + 1, rest...);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
template<typename Sign>
|
||||
struct signed_group;
|
||||
@ -149,22 +140,19 @@ namespace logid::config {
|
||||
}
|
||||
};
|
||||
|
||||
namespace {
|
||||
template<typename T>
|
||||
struct normalize_signature {
|
||||
static inline const T& make(const T& ret) { return ret; }
|
||||
static const T& make(const T& ret) { return ret; }
|
||||
};
|
||||
|
||||
template<>
|
||||
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::transform(ret.begin(), ret.end(),
|
||||
ret.begin(), ::tolower);
|
||||
std::transform(ret.begin(), ret.end(), ret.begin(), ::tolower);
|
||||
return ret;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
template<typename Sign>
|
||||
struct signed_group : public group {
|
||||
|
@ -24,13 +24,11 @@
|
||||
#include <utility>
|
||||
|
||||
namespace logid::config {
|
||||
template<size_t N>
|
||||
struct string_literal {
|
||||
constexpr string_literal(const char (& str)[N]) {
|
||||
std::copy_n(str, N, value);
|
||||
}
|
||||
struct string_literal { };
|
||||
|
||||
char value[N]{};
|
||||
template<const char* str>
|
||||
struct string_literal_of : public string_literal {
|
||||
constexpr static const char* value = str;
|
||||
};
|
||||
|
||||
template<class T>
|
||||
@ -46,10 +44,12 @@ namespace logid::config {
|
||||
};
|
||||
|
||||
// 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 Allocator=typename std::map<K, V>::allocator_type>
|
||||
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:
|
||||
template<typename... Args>
|
||||
explicit map(Args... args) :
|
||||
|
@ -51,6 +51,11 @@ namespace logid::actions {
|
||||
}
|
||||
|
||||
namespace logid::config {
|
||||
struct keys {
|
||||
static const char name[];
|
||||
static const char cid[];
|
||||
static const char direction[];
|
||||
};
|
||||
|
||||
struct NoAction : public signed_group<std::string> {
|
||||
typedef actions::NullAction action;
|
||||
@ -213,7 +218,7 @@ namespace logid::config {
|
||||
|
||||
struct GestureAction : public signed_group<std::string> {
|
||||
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;
|
||||
|
||||
GestureAction() : signed_group<std::string>(
|
||||
@ -244,9 +249,10 @@ namespace logid::config {
|
||||
struct SmartShift : public group {
|
||||
std::optional<bool> on;
|
||||
std::optional<unsigned int> threshold;
|
||||
std::optional<unsigned int> torque;
|
||||
|
||||
SmartShift() : group({"on", "threshold"},
|
||||
&SmartShift::on, &SmartShift::threshold) {}
|
||||
SmartShift() : group({"on", "threshold", "torque"},
|
||||
&SmartShift::on, &SmartShift::threshold, &SmartShift::torque) {}
|
||||
};
|
||||
|
||||
|
||||
@ -284,7 +290,7 @@ namespace logid::config {
|
||||
&ThumbWheel::tap) {}
|
||||
};
|
||||
|
||||
typedef map<uint16_t, Button, "cid"> RemapButton;
|
||||
typedef map<uint16_t, Button, string_literal_of<keys::cid>> RemapButton;
|
||||
|
||||
struct Profile : public group {
|
||||
std::optional<DPI> dpi;
|
||||
@ -302,7 +308,7 @@ namespace logid::config {
|
||||
|
||||
struct Device : public group {
|
||||
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::default_profile,
|
||||
@ -313,7 +319,7 @@ namespace logid::config {
|
||||
|
||||
struct Config : public group {
|
||||
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<double> io_timeout;
|
||||
std::optional<int> workers;
|
||||
|
@ -33,25 +33,24 @@
|
||||
namespace logid::config {
|
||||
void logError(const libconfig::Setting& setting, std::exception& e);
|
||||
|
||||
namespace {
|
||||
template<typename T>
|
||||
struct config_io {
|
||||
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) {
|
||||
T t{};
|
||||
t._load(parent.lookup(name));
|
||||
return t;
|
||||
}
|
||||
|
||||
static inline T get(const libconfig::Setting& setting) {
|
||||
static T get(const libconfig::Setting& setting) {
|
||||
T t{};
|
||||
t._load(setting);
|
||||
return t;
|
||||
}
|
||||
|
||||
static inline void set(libconfig::Setting& parent,
|
||||
static void set(libconfig::Setting& parent,
|
||||
const std::string& name,
|
||||
const T& t) {
|
||||
if (!parent.exists(name)) {
|
||||
@ -64,11 +63,11 @@ namespace logid::config {
|
||||
t._save(parent.lookup(name));
|
||||
}
|
||||
|
||||
static inline void set(libconfig::Setting& setting, const T& t) {
|
||||
static void set(libconfig::Setting& setting, const T& t) {
|
||||
t._save(setting);
|
||||
}
|
||||
|
||||
static inline void append(libconfig::Setting& list, const T& t) {
|
||||
static void append(libconfig::Setting& list, const T& t) {
|
||||
auto& x = list.add(libconfig::Setting::TypeGroup);
|
||||
set(x, t);
|
||||
}
|
||||
@ -80,16 +79,16 @@ namespace logid::config {
|
||||
|
||||
template<typename T, libconfig::Setting::Type TypeEnum>
|
||||
struct primitive_io {
|
||||
static inline T get(const libconfig::Setting& parent,
|
||||
static T get(const libconfig::Setting& parent,
|
||||
const std::string& name) {
|
||||
return parent.lookup(name);
|
||||
}
|
||||
|
||||
static inline T get(const libconfig::Setting& setting) {
|
||||
static T get(const libconfig::Setting& setting) {
|
||||
return setting;
|
||||
}
|
||||
|
||||
static inline void set(libconfig::Setting& parent,
|
||||
static void set(libconfig::Setting& parent,
|
||||
const std::string& name,
|
||||
const T& t) {
|
||||
if (!parent.exists(name)) {
|
||||
@ -101,11 +100,11 @@ namespace logid::config {
|
||||
set(parent.lookup(name), t);
|
||||
}
|
||||
|
||||
static inline void set(libconfig::Setting& setting, const T& t) {
|
||||
static void set(libconfig::Setting& setting, const T& t) {
|
||||
setting = t;
|
||||
}
|
||||
|
||||
static inline void append(libconfig::Setting& list, const T& t) {
|
||||
static void append(libconfig::Setting& list, const T& t) {
|
||||
auto& x = list.add(TypeEnum);
|
||||
set(x, t);
|
||||
}
|
||||
@ -113,29 +112,29 @@ namespace logid::config {
|
||||
|
||||
template<typename T, typename O, libconfig::Setting::Type TypeEnum>
|
||||
struct reinterpret_io {
|
||||
static inline T get(const libconfig::Setting& parent,
|
||||
static 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) {
|
||||
static T get(const libconfig::Setting& setting) {
|
||||
return static_cast<T>(primitive_io<O, TypeEnum>::get(setting));
|
||||
}
|
||||
|
||||
static inline void set(libconfig::Setting& parent,
|
||||
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 inline void set(libconfig::Setting& setting, const T& t) {
|
||||
static 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) {
|
||||
static void append(libconfig::Setting& list, const T& t) {
|
||||
primitive_io<O, TypeEnum>::append(list,
|
||||
static_cast<O>(t));
|
||||
}
|
||||
@ -203,14 +202,12 @@ namespace logid::config {
|
||||
struct config_io<std::variant<T...>> {
|
||||
private:
|
||||
template<typename Singleton>
|
||||
static inline std::variant<T...> try_each(
|
||||
const libconfig::Setting& setting) {
|
||||
static 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) {
|
||||
static std::variant<T...> try_each(const libconfig::Setting& setting) {
|
||||
try {
|
||||
return config_io<First>::get(setting);
|
||||
} catch (libconfig::SettingException& e) {
|
||||
@ -219,25 +216,23 @@ namespace logid::config {
|
||||
}
|
||||
|
||||
public:
|
||||
static inline std::variant<T...> get(
|
||||
const libconfig::Setting& setting) {
|
||||
static std::variant<T...> get(const libconfig::Setting& setting) {
|
||||
return try_each<T...>(setting);
|
||||
}
|
||||
|
||||
static inline std::variant<T...> get(
|
||||
const libconfig::Setting& parent,
|
||||
static std::variant<T...> get(const libconfig::Setting& parent,
|
||||
const std::string& name) {
|
||||
return get(parent.lookup(name));
|
||||
}
|
||||
|
||||
static inline void set(libconfig::Setting& setting,
|
||||
static void set(libconfig::Setting& setting,
|
||||
const std::variant<T...>& t) {
|
||||
std::visit([&setting](auto&& arg) {
|
||||
config::set(setting, arg);
|
||||
}, t);
|
||||
}
|
||||
|
||||
static inline void set(libconfig::Setting& parent,
|
||||
static void set(libconfig::Setting& parent,
|
||||
const std::string& name,
|
||||
const std::variant<T...>& t) {
|
||||
std::visit([&parent, &name](auto&& arg) {
|
||||
@ -246,8 +241,7 @@ namespace logid::config {
|
||||
}
|
||||
|
||||
[[maybe_unused]]
|
||||
static inline void append(libconfig::Setting& list,
|
||||
const std::variant<T...>& t) {
|
||||
static void append(libconfig::Setting& list, const std::variant<T...>& t) {
|
||||
std::visit([&list](auto&& arg) {
|
||||
config::append(list, arg);
|
||||
}, t);
|
||||
@ -256,7 +250,7 @@ namespace logid::config {
|
||||
|
||||
template<typename T>
|
||||
struct config_io<std::list<T>> {
|
||||
static inline std::list<T> get(const libconfig::Setting& setting) {
|
||||
static std::list<T> get(const libconfig::Setting& setting) {
|
||||
const auto size = setting.getLength();
|
||||
std::list<T> t{};
|
||||
for (int i = 0; i < size; ++i) {
|
||||
@ -267,13 +261,11 @@ namespace logid::config {
|
||||
return t;
|
||||
}
|
||||
|
||||
static inline std::list<T> get(const libconfig::Setting& parent,
|
||||
const std::string& name) {
|
||||
static 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) {
|
||||
static void set(libconfig::Setting& setting, const std::list<T>& t) {
|
||||
while (setting.getLength() != 0)
|
||||
setting.remove((int) 0);
|
||||
for (auto& x: t) {
|
||||
@ -281,7 +273,7 @@ namespace logid::config {
|
||||
}
|
||||
}
|
||||
|
||||
static inline void set(libconfig::Setting& parent,
|
||||
static void set(libconfig::Setting& parent,
|
||||
const std::string& name,
|
||||
const std::list<T>& t) {
|
||||
if (!parent.exists(name)) {
|
||||
@ -294,8 +286,7 @@ namespace logid::config {
|
||||
}
|
||||
|
||||
[[maybe_unused]]
|
||||
static inline void append(libconfig::Setting& list,
|
||||
const std::list<T>& t) {
|
||||
static void append(libconfig::Setting& list, const std::list<T>& t) {
|
||||
auto& s = list.add(libconfig::Setting::TypeList);
|
||||
set(s, t);
|
||||
}
|
||||
@ -303,7 +294,7 @@ namespace logid::config {
|
||||
|
||||
template<typename T>
|
||||
struct config_io<std::set<T>> {
|
||||
static inline std::set<T> get(const libconfig::Setting& setting) {
|
||||
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) {
|
||||
@ -314,13 +305,11 @@ namespace logid::config {
|
||||
return t;
|
||||
}
|
||||
|
||||
static inline std::set<T> get(const libconfig::Setting& parent,
|
||||
const std::string& name) {
|
||||
static 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) {
|
||||
static void set(libconfig::Setting& setting, const std::set<T>& t) {
|
||||
while (setting.getLength() != 0)
|
||||
setting.remove((int) 0);
|
||||
for (auto& x: t) {
|
||||
@ -329,7 +318,7 @@ namespace logid::config {
|
||||
}
|
||||
}
|
||||
|
||||
static inline void set(libconfig::Setting& parent,
|
||||
static void set(libconfig::Setting& parent,
|
||||
const std::string& name,
|
||||
const std::set<T>& t) {
|
||||
if (!parent.exists(name)) {
|
||||
@ -342,47 +331,46 @@ namespace logid::config {
|
||||
}
|
||||
|
||||
[[maybe_unused]]
|
||||
static inline void append(libconfig::Setting& list,
|
||||
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, string_literal KeyName,
|
||||
template<typename K, typename V, typename 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) {
|
||||
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)),
|
||||
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(
|
||||
static 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,
|
||||
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);
|
||||
config_io<K>::set(s, KeyName::value, x.first);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void set(libconfig::Setting& parent,
|
||||
static void set(libconfig::Setting& parent,
|
||||
const std::string& name,
|
||||
const map<K, V, KeyName, Cmp, Alloc>& t) {
|
||||
if (!parent.exists(name)) {
|
||||
@ -395,8 +383,7 @@ namespace logid::config {
|
||||
}
|
||||
|
||||
[[maybe_unused]]
|
||||
static inline void append(libconfig::Setting& list,
|
||||
const map<K, V, KeyName, Cmp, Alloc>& t) {
|
||||
static void append(libconfig::Setting& list, const map<K, V, KeyName, Cmp, Alloc>& t) {
|
||||
auto& s = list.add(libconfig::Setting::TypeList);
|
||||
set(s, t);
|
||||
}
|
||||
@ -404,7 +391,7 @@ namespace logid::config {
|
||||
|
||||
template<typename T>
|
||||
struct config_io<std::optional<T>> {
|
||||
static inline std::optional<T> get(const libconfig::Setting& parent,
|
||||
static std::optional<T> get(const libconfig::Setting& parent,
|
||||
const std::string& name) {
|
||||
if (parent.exists(name)) {
|
||||
auto& setting = parent.lookup(name);
|
||||
@ -412,14 +399,14 @@ namespace logid::config {
|
||||
return config_io<T>::get(setting);
|
||||
} catch (libconfig::SettingException& e) {
|
||||
logError(setting, e);
|
||||
return {};
|
||||
return std::nullopt;
|
||||
}
|
||||
} else {
|
||||
return {};
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void set(libconfig::Setting& parent,
|
||||
static void set(libconfig::Setting& parent,
|
||||
const std::string& name,
|
||||
const std::optional<T>& t) {
|
||||
if (t.has_value())
|
||||
@ -442,7 +429,6 @@ namespace logid::config {
|
||||
struct config_io<std::optional<std::optional<T>>> {
|
||||
static_assert(!sizeof(std::optional<T>), "Invalid type");
|
||||
};
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void set(libconfig::Setting& parent,
|
||||
|
@ -40,21 +40,19 @@ namespace logid::features {
|
||||
}
|
||||
};
|
||||
|
||||
namespace {
|
||||
template<typename T>
|
||||
class FeatureWrapper : public T {
|
||||
class _featureWrapper : public T {
|
||||
friend class DeviceFeature;
|
||||
|
||||
public:
|
||||
template<typename... Args>
|
||||
explicit FeatureWrapper(Args... args) : T(std::forward<Args>(args)...) {}
|
||||
explicit _featureWrapper(Args... args) : T(std::forward<Args>(args)...) {}
|
||||
|
||||
template<typename... 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 {
|
||||
std::weak_ptr<DeviceFeature> _self;
|
||||
@ -68,7 +66,9 @@ namespace logid::features {
|
||||
virtual ~DeviceFeature() = default;
|
||||
|
||||
DeviceFeature(const DeviceFeature&) = delete;
|
||||
|
||||
DeviceFeature(DeviceFeature&&) = delete;
|
||||
|
||||
protected:
|
||||
explicit DeviceFeature(Device* dev) : _device(dev) {}
|
||||
|
||||
@ -82,7 +82,7 @@ namespace logid::features {
|
||||
public:
|
||||
template<typename T, typename... 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;
|
||||
|
||||
return feature;
|
||||
|
@ -63,7 +63,7 @@ RemapButton::RemapButton(Device* dev) : DeviceFeature(dev),
|
||||
if ((action->reprogFlags() & hidpp20::ReprogControls::RawXYDiverted) &&
|
||||
(!_reprog_controls->supportsRawXY() ||
|
||||
!(info.additionalFlags & hidpp20::ReprogControls::RawXY)))
|
||||
logPrintf(WARN, "%s: Cannot divert raw XY movements for CID 0x%02x",
|
||||
logPrintf(WARN, "%s: 'Cannot divert raw XY movements for CID 0x%02x",
|
||||
_device->name().c_str(), info.controlID);
|
||||
|
||||
report.flags |= action->reprogFlags();
|
||||
@ -220,8 +220,9 @@ void Button::_makeConfig() {
|
||||
}
|
||||
}
|
||||
|
||||
void Button::press() const {
|
||||
void Button::press() {
|
||||
std::shared_lock lock(_action_lock);
|
||||
_first_move = true;
|
||||
if (_action)
|
||||
_action->press();
|
||||
}
|
||||
@ -232,10 +233,12 @@ void Button::release() const {
|
||||
_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);
|
||||
if (_action)
|
||||
if (_action && !_first_move)
|
||||
_action->move(x, y);
|
||||
else if (_first_move)
|
||||
_first_move = false;
|
||||
}
|
||||
|
||||
bool Button::pressed() const {
|
||||
|
@ -36,11 +36,11 @@ namespace logid::features {
|
||||
Info info, int index, Device* device, ConfigFunction conf_func,
|
||||
const std::shared_ptr<ipcgull::node>& root, config::Button& config);
|
||||
|
||||
void press() const;
|
||||
void press();
|
||||
|
||||
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);
|
||||
|
||||
@ -82,6 +82,8 @@ namespace logid::features {
|
||||
std::shared_ptr<actions::Action> _action;
|
||||
const Info _info;
|
||||
|
||||
bool _first_move{};
|
||||
|
||||
std::weak_ptr<Button> _self;
|
||||
|
||||
std::shared_ptr<IPC> _ipc_interface;
|
||||
|
@ -25,11 +25,35 @@ using namespace logid::backend;
|
||||
SmartShift::SmartShift(Device* device) : DeviceFeature(device),
|
||||
_config(device->activeProfile().smartshift) {
|
||||
try {
|
||||
_smartshift = std::make_shared<hidpp20::SmartShift>(&device->hidpp20());
|
||||
_smartshift = hidpp20::SmartShift::autoVersion(&device->hidpp20());
|
||||
} catch (hidpp20::UnsupportedFeature& e) {
|
||||
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);
|
||||
}
|
||||
|
||||
@ -45,6 +69,9 @@ void SmartShift::configure() {
|
||||
settings.setAutoDisengage = conf.threshold.has_value();
|
||||
if (settings.setAutoDisengage)
|
||||
settings.autoDisengage = conf.threshold.value();
|
||||
settings.setTorque = conf.torque.has_value();
|
||||
if (settings.setTorque)
|
||||
settings.torque = conf.torque.value();
|
||||
|
||||
_smartshift->setStatus(settings);
|
||||
}
|
||||
@ -66,86 +93,108 @@ void SmartShift::setStatus(Status 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) :
|
||||
ipcgull::interface(
|
||||
SERVICE_ROOT_NAME ".SmartShift", {
|
||||
{"GetStatus", {this, &IPC::getStatus, {"active", "threshold"}}},
|
||||
{"SetActive", {this, &IPC::setActive, {"active"}}},
|
||||
{"SetThreshold", {this, &IPC::setThreshold, {"threshold"}}},
|
||||
{"GetDefault", {this, &IPC::getDefault, {"setActive", "active", "setThreshold", "threshold"}}},
|
||||
{"ClearDefaultActive", {this, &IPC::clearDefaultActive}},
|
||||
{"SetDefaultActive", {this, &IPC::setDefaultActive, {"active"}}},
|
||||
{"ClearDefaultThreshold", {this, &IPC::clearDefaultThreshold}},
|
||||
{"SetDefaultThreshold", {this, &IPC::setDefaultThreshold, {"threshold"}}}
|
||||
}, {}, {}),
|
||||
{"GetConfig", {this, &IPC::getConfig, {"active", "threshold", "torque"}}},
|
||||
{"SetActive", {this, &IPC::setActive, {"active", "clear"}}},
|
||||
{"SetThreshold", {this, &IPC::setThreshold, {"threshold", "clear"}}},
|
||||
{"SetTorque", {this, &IPC::setTorque, {"torque", "clear"}}},
|
||||
},
|
||||
{
|
||||
{"TorqueSupport", ipcgull::property<bool>(
|
||||
ipcgull::property_readable, parent->supportsTorque())},
|
||||
}, {}),
|
||||
_parent(*parent) {
|
||||
}
|
||||
|
||||
std::tuple<bool, uint8_t> SmartShift::IPC::getStatus() const {
|
||||
auto ret = _parent.getStatus();
|
||||
return std::make_tuple(ret.active, ret.autoDisengage);
|
||||
}
|
||||
|
||||
void SmartShift::IPC::setActive(bool active) {
|
||||
Status status{};
|
||||
status.setActive = true;
|
||||
status.active = active;
|
||||
_parent.setStatus(status);
|
||||
}
|
||||
|
||||
void SmartShift::IPC::setThreshold(uint8_t threshold) {
|
||||
Status status{};
|
||||
status.setAutoDisengage = true;
|
||||
status.autoDisengage = threshold;
|
||||
_parent.setStatus(status);
|
||||
}
|
||||
|
||||
std::tuple<bool, bool, bool, uint8_t> SmartShift::IPC::getDefault() const {
|
||||
std::tuple<uint8_t, uint8_t, uint8_t> SmartShift::IPC::getConfig() const {
|
||||
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;
|
||||
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::clearDefaultActive() {
|
||||
void SmartShift::IPC::setActive(bool active, bool clear) {
|
||||
std::unique_lock lock(_parent._config_mutex);
|
||||
auto& config = _parent._config.get();
|
||||
if (clear) {
|
||||
if (config.has_value())
|
||||
config.value().on.reset();
|
||||
}
|
||||
|
||||
void SmartShift::IPC::setDefaultActive(bool active) {
|
||||
std::unique_lock lock(_parent._config_mutex);
|
||||
auto& config = _parent._config.get();
|
||||
} 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::clearDefaultThreshold() {
|
||||
void SmartShift::IPC::setThreshold(uint8_t threshold, bool clear) {
|
||||
std::unique_lock lock(_parent._config_mutex);
|
||||
auto& config = _parent._config.get();
|
||||
Status status{};
|
||||
status.setAutoDisengage = true;
|
||||
|
||||
/* clip threshold */
|
||||
if (threshold == 0)
|
||||
threshold = 1;
|
||||
|
||||
if (clear) {
|
||||
if (config.has_value())
|
||||
config.value().threshold.reset();
|
||||
}
|
||||
|
||||
void SmartShift::IPC::setDefaultThreshold(uint8_t threshold) {
|
||||
std::unique_lock lock(_parent._config_mutex);
|
||||
auto& config = _parent._config.get();
|
||||
status.autoDisengage = _parent.getDefaults().autoDisengage;
|
||||
} else {
|
||||
if (!config.has_value())
|
||||
config = config::SmartShift{};
|
||||
config.value().threshold = threshold;
|
||||
status.autoDisengage = threshold;
|
||||
}
|
||||
_parent.setStatus(status);
|
||||
}
|
||||
|
||||
void SmartShift::IPC::setTorque(uint8_t torque, bool clear) {
|
||||
std::unique_lock lock(_parent._config_mutex);
|
||||
auto& config = _parent._config.get();
|
||||
Status status{};
|
||||
status.setTorque = true;
|
||||
|
||||
/* clip torque */
|
||||
if (torque == 0)
|
||||
torque = 1;
|
||||
else if (torque > 100)
|
||||
torque = 100;
|
||||
|
||||
if (!_parent.supportsTorque())
|
||||
throw std::invalid_argument("torque unsupported");
|
||||
|
||||
if (clear) {
|
||||
if (config.has_value())
|
||||
config.value().torque.reset();
|
||||
status.torque = _parent.getDefaults().torque;
|
||||
} else {
|
||||
if (!config.has_value())
|
||||
config = config::SmartShift{};
|
||||
config.value().torque = torque;
|
||||
status.torque = torque;
|
||||
}
|
||||
_parent.setStatus(status);
|
||||
}
|
||||
|
@ -27,19 +27,22 @@
|
||||
namespace logid::features {
|
||||
class SmartShift : public DeviceFeature {
|
||||
public:
|
||||
|
||||
void configure() final;
|
||||
|
||||
void listen() final;
|
||||
|
||||
void setProfile(config::Profile& profile) final;
|
||||
|
||||
typedef backend::hidpp20::SmartShift::SmartshiftStatus Status;
|
||||
typedef backend::hidpp20::SmartShift::Status Status;
|
||||
|
||||
[[nodiscard]] Status getStatus() const;
|
||||
|
||||
void setStatus(Status status);
|
||||
|
||||
[[nodiscard]] const backend::hidpp20::SmartShift::Defaults& getDefaults() const;
|
||||
|
||||
[[nodiscard]] bool supportsTorque() const;
|
||||
|
||||
protected:
|
||||
explicit SmartShift(Device* dev);
|
||||
|
||||
@ -48,25 +51,20 @@ namespace logid::features {
|
||||
std::reference_wrapper<std::optional<config::SmartShift>> _config;
|
||||
std::shared_ptr<backend::hidpp20::SmartShift> _smartshift;
|
||||
|
||||
backend::hidpp20::SmartShift::Defaults _defaults{};
|
||||
bool _torque_support = false;
|
||||
|
||||
class IPC : public ipcgull::interface {
|
||||
public:
|
||||
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 clearDefaultActive();
|
||||
|
||||
void setDefaultActive(bool active);
|
||||
|
||||
void clearDefaultThreshold();
|
||||
|
||||
void setDefaultThreshold(uint8_t threshold);
|
||||
void setTorque(uint8_t torque, bool clear);
|
||||
|
||||
private:
|
||||
SmartShift& _parent;
|
||||
|
@ -135,12 +135,18 @@ int main(int argc, char** argv) {
|
||||
std::shared_ptr<Configuration> config;
|
||||
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
|
||||
try {
|
||||
config = std::make_shared<Configuration>(options.config_file);
|
||||
}
|
||||
catch (std::exception& e) {
|
||||
config = std::make_shared<Configuration>();
|
||||
} catch (std::exception &e) {
|
||||
logPrintf(ERROR, "%s", e.what());
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
init_workers(config->workers.value_or(defaults::workers));
|
||||
|
@ -8,7 +8,6 @@ Wants=multi-user.target
|
||||
Type=simple
|
||||
ExecStart=${CMAKE_INSTALL_PREFIX}/bin/logid
|
||||
User=root
|
||||
Restart=on-failure
|
||||
|
||||
[Install]
|
||||
WantedBy=graphical.target
|
||||
|
@ -3,11 +3,12 @@
|
||||
"http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
|
||||
|
||||
<busconfig>
|
||||
<policy user="root">
|
||||
<allow own="pizza.pixl.LogiOps"/>
|
||||
<policy context="default">
|
||||
<deny receive_sender="pizza.pixl.LogiOps"/>
|
||||
</policy>
|
||||
|
||||
<policy context="default">
|
||||
<policy user="root">
|
||||
<allow own="pizza.pixl.LogiOps"/>
|
||||
<allow send_destination="pizza.pixl.LogiOps"/>
|
||||
<allow receive_sender="pizza.pixl.LogiOps"/>
|
||||
</policy>
|
||||
|
@ -18,6 +18,7 @@
|
||||
#include <util/task.h>
|
||||
#include <queue>
|
||||
#include <optional>
|
||||
#include <cassert>
|
||||
|
||||
using namespace logid;
|
||||
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::condition_variable task_cv {};
|
||||
static std::atomic_bool workers_init = false;
|
||||
static std::atomic_bool workers_run = false;
|
||||
|
||||
[[noreturn]] static void worker() {
|
||||
void stop_workers() {
|
||||
std::unique_lock lock(task_mutex);
|
||||
while (true) {
|
||||
task_cv.wait(lock, []() { return !tasks.empty(); });
|
||||
if (workers_init) {
|
||||
workers_run = false;
|
||||
lock.unlock();
|
||||
task_cv.notify_all();
|
||||
|
||||
/* Wait for all workers to end */
|
||||
lock.lock();
|
||||
}
|
||||
}
|
||||
|
||||
void worker() {
|
||||
std::unique_lock lock(task_mutex);
|
||||
while (workers_run) {
|
||||
task_cv.wait(lock, []() { return !tasks.empty() || !workers_run; });
|
||||
|
||||
if (!workers_run)
|
||||
break;
|
||||
|
||||
/* top task is in the future, wait */
|
||||
if (tasks.top().time >= system_clock::now()) {
|
||||
auto wait = tasks.top().time - system_clock::now();
|
||||
task_cv.wait_for(lock, wait, []() {
|
||||
return !tasks.empty() && (tasks.top().time < system_clock::now());
|
||||
return (!tasks.empty() && (tasks.top().time < system_clock::now())) ||
|
||||
!workers_run;
|
||||
});
|
||||
|
||||
if (!workers_run)
|
||||
break;
|
||||
}
|
||||
|
||||
if (!tasks.empty()) {
|
||||
@ -67,11 +88,15 @@ static std::atomic_bool workers_init = false;
|
||||
|
||||
void logid::init_workers(int worker_count) {
|
||||
std::lock_guard lock(task_mutex);
|
||||
assert(!workers_init);
|
||||
|
||||
for (int i = 0; i < worker_count; ++i)
|
||||
std::thread(&worker).detach();
|
||||
|
||||
workers_init = true;
|
||||
workers_run = true;
|
||||
|
||||
atexit(&stop_workers);
|
||||
}
|
||||
|
||||
void logid::run_task(std::function<void()> function) {
|
||||
|
@ -1 +0,0 @@
|
||||
v0.3.0
|
Loading…
Reference in New Issue
Block a user