mirror of https://github.com/nirenjan/libx52.git
Compare commits
77 Commits
| Author | SHA1 | Date |
|---|---|---|
|
|
aa555f5e66 | |
|
|
842e7e53ed | |
|
|
dfbe3e6d21 | |
|
|
732bc21b65 | |
|
|
3c1abd57d5 | |
|
|
b0b457d14e | |
|
|
ae077dbed8 | |
|
|
d29be6213f | |
|
|
273ed22f8e | |
|
|
cdb00739ca | |
|
|
08a6b0a736 | |
|
|
45d561e0d8 | |
|
|
b626a9367f | |
|
|
a1098bc134 | |
|
|
569902be76 | |
|
|
0cb137bbe0 | |
|
|
899ea57bf7 | |
|
|
e1e020a4f5 | |
|
|
7cbf091dc7 | |
|
|
9d180531b9 | |
|
|
74229b391d | |
|
|
5f8177f16b | |
|
|
c5ec15231f | |
|
|
33bbafe970 | |
|
|
e9a806a6a2 | |
|
|
ad30bfff7b | |
|
|
cccb561020 | |
|
|
6743c60dfd | |
|
|
b4ec8d4629 | |
|
|
3fb0d72124 | |
|
|
74fe559f4a | |
|
|
b6e61fc54e | |
|
|
e479e338a2 | |
|
|
69ae9626c7 | |
|
|
e9f4e1b3a8 | |
|
|
f43ba6b902 | |
|
|
378cbbd931 | |
|
|
47da6e22d1 | |
|
|
e98b8b4bc3 | |
|
|
7b7065f8f0 | |
|
|
2fa9f52ddb | |
|
|
a17312dcbc | |
|
|
2be7792024 | |
|
|
f51b777ca0 | |
|
|
b3dff7182b | |
|
|
0356a2d610 | |
|
|
c1e3c85738 | |
|
|
accd2a1f4e | |
|
|
e8208e97cb | |
|
|
421e2964b3 | |
|
|
2378ba7dc4 | |
|
|
762a3468b2 | |
|
|
ef4cbee127 | |
|
|
c63b924705 | |
|
|
1b00bf4a69 | |
|
|
a40546bda3 | |
|
|
108293abdf | |
|
|
9361c7af5c | |
|
|
004eca2418 | |
|
|
1902ca0d27 | |
|
|
6330d28c4d | |
|
|
5c37c4a9db | |
|
|
863e43e4ad | |
|
|
49c57f4a6a | |
|
|
b0b9123a2e | |
|
|
50a911160f | |
|
|
7a56af032b | |
|
|
c46cec3138 | |
|
|
21050e40a8 | |
|
|
9e2e8cb8ff | |
|
|
5f4dfe4c01 | |
|
|
0870518598 | |
|
|
d7b4a694fa | |
|
|
326ac992ac | |
|
|
d3973a0abf | |
|
|
ebca9566d7 | |
|
|
03c0376e7c |
|
|
@ -1 +1,5 @@
|
|||
/version-info ident
|
||||
*/meson.build ident
|
||||
/.github/ export-ignore
|
||||
.gitignore export-ignore
|
||||
.gitattributes export-ignore
|
||||
|
|
|
|||
|
|
@ -0,0 +1,8 @@
|
|||
version: 2
|
||||
updates:
|
||||
# Maintain dependencies for Github Actions
|
||||
- package-ecosystem: "github-actions"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "daily"
|
||||
open-pull-requests-limit: 5
|
||||
|
|
@ -2,16 +2,14 @@
|
|||
# Run the build and tests
|
||||
set -e
|
||||
|
||||
./autogen.sh
|
||||
mkdir build
|
||||
meson setup -Dprefix=/usr -Dsysconfdir=/etc -Dlocalstatedir=/var -Dnls=enabled build
|
||||
cd build
|
||||
../configure
|
||||
make -j V=0
|
||||
make -j check V=0
|
||||
make -j distcheck
|
||||
meson compile
|
||||
meson test
|
||||
meson dist
|
||||
|
||||
# Print bugreport output
|
||||
./x52bugreport
|
||||
./bugreport/x52bugreport
|
||||
|
||||
# Make sure that there are no changes to the source code
|
||||
# This may happen if the source have changed with differences to the
|
||||
|
|
|
|||
|
|
@ -2,8 +2,6 @@
|
|||
# Generate Doxygen documentation
|
||||
set -e
|
||||
|
||||
./autogen.sh
|
||||
mkdir build
|
||||
meson setup -Dprefix=/usr -Dsysconfdir=/etc -Dlocalstatedir=/var -Dnls=enabled build
|
||||
cd build
|
||||
../configure --prefix=/usr --sysconfdir=/etc --localstatedir=/var
|
||||
make docs/.stamp
|
||||
ninja docs
|
||||
|
|
|
|||
|
|
@ -0,0 +1,38 @@
|
|||
#!/usr/bin/env python3
|
||||
"""Generate a changelog for the latest release and dump it to stdout"""
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
def get_git_root():
|
||||
script_path = Path(__file__).resolve()
|
||||
|
||||
# This is always going to reside at <git-root>/.github/scripts/*.py
|
||||
scripts_dir = Path(script_path.parent)
|
||||
gh_dir = Path(scripts_dir.parent)
|
||||
|
||||
return Path(gh_dir.parent)
|
||||
|
||||
def main():
|
||||
git_root = get_git_root()
|
||||
|
||||
changelog_file = git_root / 'ChangeLog.md'
|
||||
|
||||
latest = False
|
||||
with open(changelog_file) as cfd:
|
||||
for line in cfd:
|
||||
if line.startswith('## '):
|
||||
if 'Unreleased' in line:
|
||||
continue
|
||||
|
||||
if latest:
|
||||
break
|
||||
|
||||
latest = True
|
||||
continue
|
||||
|
||||
if latest:
|
||||
print(line, end='')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
|
@ -9,7 +9,11 @@ brew install \
|
|||
gettext \
|
||||
libusb \
|
||||
hidapi \
|
||||
inih \
|
||||
doxygen \
|
||||
cmocka
|
||||
cmocka \
|
||||
meson \
|
||||
ninja \
|
||||
inih
|
||||
|
||||
exit 0
|
||||
|
|
|
|||
|
|
@ -12,8 +12,12 @@ sudo apt-get install -y \
|
|||
libusb-1.0-0-dev \
|
||||
libhidapi-dev \
|
||||
libevdev-dev \
|
||||
libinih-dev \
|
||||
doxygen \
|
||||
libcmocka-dev \
|
||||
faketime
|
||||
faketime \
|
||||
meson \
|
||||
ninja-build \
|
||||
libinih-dev
|
||||
|
||||
exit 0
|
||||
|
|
|
|||
|
|
@ -1,5 +1,8 @@
|
|||
name: Build/Test
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
|
|
@ -15,18 +18,18 @@ jobs:
|
|||
if: "!(contains(github.event.head_commit.message, '[ci skip]') || contains(github.event.head_commit.message, '[skip ci]'))"
|
||||
name: ${{ join(matrix.*, '/') }}
|
||||
runs-on: ${{ matrix.os }}
|
||||
continue-on-error: ${{ startsWith(matrix.os, 'macos-') || (matrix.os == 'ubuntu-22.04') }}
|
||||
continue-on-error: ${{ startsWith(matrix.os, 'macos-') }}
|
||||
env:
|
||||
CC: ${{ matrix.cc }}
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
os: ['ubuntu-20.04', 'ubuntu-22.04', 'macos-11', 'macos-12']
|
||||
os: ['ubuntu-22.04', 'ubuntu-24.04', 'macos-latest']
|
||||
cc: ['gcc', 'clang']
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Install dependencies (Ubuntu)
|
||||
run: ./.github/scripts/install-dependencies-ubuntu.sh
|
||||
|
|
|
|||
|
|
@ -1,5 +1,9 @@
|
|||
name: "CodeQL"
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
security-events: write
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
# The branches below must be a subset of the branches above
|
||||
|
|
@ -14,7 +18,7 @@ jobs:
|
|||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
# We must fetch at least the immediate parents so that if this is
|
||||
# a pull request then we can checkout the head.
|
||||
|
|
@ -30,26 +34,13 @@ jobs:
|
|||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v2
|
||||
uses: github/codeql-action/init@v4
|
||||
# Override language selection by uncommenting this and choosing your languages
|
||||
# with:
|
||||
# languages: go, javascript, csharp, python, cpp, java
|
||||
|
||||
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
||||
# If this step fails, then you should remove it and run the build manually (see below)
|
||||
- name: Autobuild
|
||||
uses: github/codeql-action/autobuild@v2
|
||||
|
||||
# ℹ️ Command-line programs to run using the OS shell.
|
||||
# 📚 https://git.io/JvXDl
|
||||
|
||||
# ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
|
||||
# and modify them (or add more) to build your code if your project
|
||||
# uses a compiled language
|
||||
|
||||
#- run: |
|
||||
# make bootstrap
|
||||
# make release
|
||||
- name: Build
|
||||
run: ./.github/scripts/build-and-test.sh
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v2
|
||||
uses: github/codeql-action/analyze@v4
|
||||
|
|
|
|||
|
|
@ -0,0 +1,44 @@
|
|||
name: Code Coverage
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- '*'
|
||||
- '!gh-pages'
|
||||
pull_request:
|
||||
branches:
|
||||
- 'master'
|
||||
|
||||
jobs:
|
||||
coverage:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
./.github/scripts/install-dependencies-ubuntu.sh
|
||||
sudo apt-get install -y gcovr
|
||||
|
||||
- name: Configure and Build
|
||||
run: |
|
||||
meson setup build -Db_coverage=true --buildtype=debug
|
||||
meson compile -C build
|
||||
|
||||
- name: Run Tests
|
||||
run: meson test -C build
|
||||
|
||||
- name: Generate Coverage Report
|
||||
run: |
|
||||
# This generates the XML report for the upload step
|
||||
ninja -C build coverage-xml
|
||||
|
||||
- name: Upload Report to Codecov
|
||||
uses: codecov/codecov-action@v5
|
||||
with:
|
||||
files: buildd/meson-logs/coverage.xml
|
||||
fail_ci_if_error: true
|
||||
|
|
@ -5,14 +5,22 @@ on:
|
|||
branches:
|
||||
- 'master'
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
pages: write # Required to push to the Pages server
|
||||
id-token: write # Required to verify the deployment is legitimate
|
||||
|
||||
jobs:
|
||||
doxygen:
|
||||
if: "!(contains(github.event.head_commit.message, '[doxy skip]') || contains(github.event.head_commit.message, '[skip doxy]'))"
|
||||
runs-on: 'ubuntu-latest'
|
||||
environment:
|
||||
name: github-pages
|
||||
url: ${{ steps.deployment.outputs.page_url }}
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Install dependencies
|
||||
run: ./.github/scripts/install-dependencies-ubuntu.sh
|
||||
|
|
@ -23,8 +31,11 @@ jobs:
|
|||
- name: Dump generated files
|
||||
run: find ./build -type f -print
|
||||
|
||||
- name: Deploy generated documentation to Github pages
|
||||
uses: peaceiris/actions-gh-pages@v3
|
||||
- name: Upload built pages
|
||||
uses: actions/upload-pages-artifact@v4
|
||||
with:
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
publish_dir: ./build/docs/html
|
||||
path: './build/docs/html'
|
||||
|
||||
- name: Deploy to GitHub Pages
|
||||
id: deployment
|
||||
uses: actions/deploy-pages@v4
|
||||
|
|
|
|||
|
|
@ -1,5 +1,8 @@
|
|||
name: Kernel Module
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ '*' ]
|
||||
|
|
@ -16,11 +19,11 @@ jobs:
|
|||
|
||||
strategy:
|
||||
matrix:
|
||||
os: ['ubuntu-18.04', 'ubuntu-20.04']
|
||||
os: ['ubuntu-22.04', 'ubuntu-24.04']
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Install kernel dependencies
|
||||
run: ./.github/scripts/install-kernel-dependencies.sh
|
||||
|
|
|
|||
|
|
@ -1,5 +1,8 @@
|
|||
name: Create Release
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
|
|
@ -11,7 +14,7 @@ jobs:
|
|||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Install dependencies
|
||||
run: ./.github/scripts/install-dependencies-ubuntu.sh
|
||||
|
|
@ -19,30 +22,40 @@ jobs:
|
|||
- name: Build project
|
||||
run: ./.github/scripts/build-and-test.sh
|
||||
|
||||
- name: Find release tarball
|
||||
id: find_release
|
||||
- name: Prepare release assets
|
||||
run: |
|
||||
echo "::set-output name=path::$(find $PWD -name 'libx52*.tar*')"
|
||||
echo "::set-output name=asset::$(find . -name 'libx52*.tar*' -exec basename {} \; | sed 's/libx52-\(.*\)\.tar/libx52_\1.orig.tar/')"
|
||||
# Find the Meson generated tarball
|
||||
# meson dist usuall creates a tar.xz, but be prepared to handle
|
||||
# additional compression formats
|
||||
DIST_FILE=$(find build/meson-dist -name 'libx52-*.tar.*' -a ! -name '*.tar.*sum')
|
||||
|
||||
# Extract the version from the filename
|
||||
VERSION=$(echo "$DIST_FILE" | grep -oE '[0-9]+\.[0-9]+\.[0-9]+')
|
||||
EXTENSION="${DIST_FILE#*${VERSION}}"
|
||||
|
||||
ASSET_NAME="libx52_${VERSION}.orig${EXTENSION}"
|
||||
ASSET_PATH="build/meson-dist/${ASSET_NAME}"
|
||||
|
||||
# Rename the file
|
||||
mv -v "$DIST_FILE" "$ASSET_PATH"
|
||||
|
||||
cd build/meson-dist
|
||||
rm *.sha256sum
|
||||
sha256sum "$ASSET_NAME" > "${ASSET_NAME}.sha256sum"
|
||||
cd ../..
|
||||
|
||||
- name: Generate changelog
|
||||
run: ./.github/scripts/generate_changelog.py > ${{ github.workspace }}/CHANGELOG.txt
|
||||
|
||||
- name: Create Release
|
||||
id: create_release
|
||||
uses: actions/create-release@v1
|
||||
uses: softprops/action-gh-release@v2
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
tag_name: ${{ github.ref }}
|
||||
release_name: Release ${{ github.ref }}
|
||||
name: Release ${{ github.ref_name }}
|
||||
body_path: ${{ github.workspace }}/CHANGELOG.txt
|
||||
draft: false
|
||||
prerelease: false
|
||||
|
||||
- name: Upload Release Tarball
|
||||
id: upload-release-tarball
|
||||
uses: actions/upload-release-asset@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||
asset_path: ${{ steps.find_release.outputs.path }}
|
||||
asset_name: ${{ steps.find_release.outputs.asset }}
|
||||
asset_content_type: application/gzip
|
||||
files: |
|
||||
build/meson-dist/*
|
||||
|
|
|
|||
68
ChangeLog.md
68
ChangeLog.md
|
|
@ -6,6 +6,68 @@ The format is based upon [Keep a Changelog].
|
|||
|
||||
## [Unreleased]
|
||||
|
||||
### Fixed
|
||||
- Addressed meson build bugs found in v0.3.3
|
||||
|
||||
## [0.3.3] - 2026-03-12
|
||||
|
||||
**Note:** While this release does introduce Meson support and deprecate the
|
||||
Autotools framework, there are several bugs in the Meson build files, causing a
|
||||
number of missing builds, notably the systemd service file, the documentation
|
||||
and man pages. In addition, the translation files were not handled at all, and
|
||||
translation services were disabled by default, compared to the Autotools
|
||||
framework where it was enabled by default. These bugs will be addressed in the
|
||||
next release.
|
||||
|
||||
### Added
|
||||
- Updated build infrastructure to use Meson instead of Autotools.
|
||||
- Added a [privacy policy](PRIVACY.md) to comply with GDPR/CCPA regulations.
|
||||
- Added a [security policy](SECURITY.md) to help securely report vulnerabilities.
|
||||
- Added Dependabot configuration to keep Action files up to date.
|
||||
- Added a changelog script to automatically generate the latest release changelog.
|
||||
|
||||
### Changed
|
||||
- **BREAKING**: Removed vendored inih package and switched build framework to use inih from the system package manager.
|
||||
- `x52bugreport` tool now strips out potentially identifying information.
|
||||
- Removed the use of a 3rd party action to deploy generated Doxygen pages to the gh-pages branch. This now uses the modern gh-pages deployment action.
|
||||
- Updated release action to use softprops/action-gh-release@v2, since the original actions are no longer maintained.
|
||||
|
||||
### Deprecated
|
||||
- Autotools build framework is now deprecated, and will be removed in the next release.
|
||||
|
||||
### Fixed
|
||||
- Github Actions updated to use current set of runners
|
||||
- Fixed handling malformed UTF-8 input in libx52util
|
||||
- Fixed boundary check issue in libx52util that incorrectly returned `-E2BIG` if the output buffer was the exact size to capture the translated string and the null terminator.
|
||||
- Fixed potential UB in libx52-string-test
|
||||
- Fixed NULL pointer dereference in `libx52_exit`
|
||||
- Fixed errors identified by the GCC `-fanalyzer` flag
|
||||
|
||||
### Security
|
||||
- Updated action files to include permission blocks
|
||||
|
||||
## [0.3.2] - 2024-06-09
|
||||
### Added
|
||||
- Updated bug report utility to add details about build host details and
|
||||
compiler information.
|
||||
|
||||
### Fixed
|
||||
- Updated syntax check for calloc calls. See
|
||||
[#52](https://github.com/nirenjan/libx52/issues/52)
|
||||
- Fixed a tooling bug where running make check on a system without cmocka
|
||||
library installed would fail during daemon testing.
|
||||
- Cleaned up daemon protocol documentation
|
||||
|
||||
### Changed
|
||||
- Moved socket code around to make it easier to reuse the communication logic
|
||||
out in both client(s) and server.
|
||||
|
||||
## 0.3.1 - 2024-06-08
|
||||
|
||||
**Important:** Tag 0.3.1 has a bad Version file and should not be used. This has
|
||||
been superseded by 0.3.2 with corrected metadata. The changes from the previous
|
||||
release are the same.
|
||||
|
||||
## [0.3.0] - 2022-12-25
|
||||
### Added
|
||||
- Bug report utility to make it easier to gather system and build information
|
||||
|
|
@ -158,8 +220,10 @@ The format is based upon [Keep a Changelog].
|
|||
[Keep a Changelog]: http://keepachangelog.com/en/1.0.0/
|
||||
[Semantic Versioning]: http://semver.org/spec/v2.0.0.html
|
||||
[TAP]: https://testanything.org
|
||||
[Unreleased]: https://github.com/nirenjan/libx52/compare/v0.3.0...HEAD
|
||||
[0.2.3]: https://github.com/nirenjan/libx52/compare/v0.2.3...v0.3.0
|
||||
[Unreleased]: https://github.com/nirenjan/libx52/compare/v0.3.3...HEAD
|
||||
[0.3.3]: https://github.com/nirenjan/libx52/compare/v0.3.2...v0.3.3
|
||||
[0.3.2]: https://github.com/nirenjan/libx52/compare/v0.3.0...v0.3.2
|
||||
[0.3.0]: https://github.com/nirenjan/libx52/compare/v0.2.3...v0.3.0
|
||||
[0.2.3]: https://github.com/nirenjan/libx52/compare/v0.2.2...v0.2.3
|
||||
[0.2.2]: https://github.com/nirenjan/libx52/compare/v0.2.1...v0.2.2
|
||||
[0.2.1]: https://github.com/nirenjan/libx52/compare/v0.2.0...v0.2.1
|
||||
|
|
|
|||
842
Doxyfile.in
842
Doxyfile.in
File diff suppressed because it is too large
Load Diff
49
INSTALL.md
49
INSTALL.md
|
|
@ -3,23 +3,20 @@ Installation instructions for libx52
|
|||
|
||||
Build has been tested on the following operating systems (x86-64 only):
|
||||
|
||||
* Ubuntu 20.04 LTS
|
||||
* Ubuntu 22.04 LTS
|
||||
* macOS Big Sur 11
|
||||
* macOS Monterey 12
|
||||
* Ubuntu 24.04 LTS
|
||||
* macOS (latest tag on Github, ARM)
|
||||
|
||||
# Prerequisites
|
||||
|
||||
## Required Packages
|
||||
|
||||
* automake
|
||||
* autoconf
|
||||
* autopoint
|
||||
* meson
|
||||
* ninja
|
||||
* gettext
|
||||
* hidapi + headers
|
||||
* libtool
|
||||
* inih
|
||||
* libusb-1.0 + headers
|
||||
* libevdev + headers (on Linux)
|
||||
* pkg-config
|
||||
* python3 (3.6 or greater)
|
||||
* git (not required for builds, but necessary to clone the repository)
|
||||
|
|
@ -28,15 +25,16 @@ Build has been tested on the following operating systems (x86-64 only):
|
|||
|
||||
| Platform | Install instructions |
|
||||
| -------- | -------------------- |
|
||||
| Ubuntu | `sudo apt-get install automake autoconf gettext autopoint libhidapi-dev libevdev-dev libtool libusb-1.0-0-dev pkg-config python3 git` |
|
||||
| MacOS + Homebrew | `brew install automake autoconf gettext hidapi libtool libusb pkg-config python3 git` |
|
||||
| Arch Linux | `pacman -S base-devel libusb hidapi libevdev python git` |
|
||||
| Fedora | `sudo dnf install autoconf automake gettext-devel findutils libtool hidapi-devel libusb-devel libevdev-devel pkg-config python3 git` |
|
||||
| Ubuntu | `sudo apt-get install meson gettext libhidapi-dev libevdev-dev libusb-1.0-0-dev libinih-dev pkg-config python3 git` |
|
||||
| MacOS + Homebrew | `brew install meson gettext hidapi libtool libusb pkg-config python3 git` |
|
||||
| Arch Linux | `pacman -S base-devel meson libusb hidapi libevdev libinih python git` |
|
||||
| Fedora | `sudo dnf install meson gettext-devel findutils hidapi-devel libusb-devel libevdev-devel inih-devel pkg-config python3 git` |
|
||||
|
||||
## Optional Packages
|
||||
|
||||
* doxygen - to generate HTML documentation and man pages
|
||||
* libcmocka (1.1 or greater) + headers - to run unit tests
|
||||
* libevdev + headers (on Linux) - to add virtual keyboard/mouse support
|
||||
|
||||
# Installation Instructions
|
||||
|
||||
|
|
@ -48,37 +46,36 @@ git clone https://github.com/nirenjan/libx52.git
|
|||
2. Run autogen.sh
|
||||
```
|
||||
cd ./libx52
|
||||
./autogen.sh
|
||||
meson setup build -Dprefix=/usr
|
||||
```
|
||||
|
||||
3. Run the following commands:
|
||||
```
|
||||
./configure --prefix=/usr --localstatedir=/var --sysconfdir=/etc
|
||||
make && sudo make install
|
||||
meson compile -C build && meson install -C build
|
||||
```
|
||||
|
||||
You may want to remove or edit the `--prefix=/usr` option, most users prefer
|
||||
non-distro binaries in `/usr/local` (default without `--prefix`) or `/opt`.
|
||||
You may want to remove or edit the `-Dprefix=/usr` option, most users prefer
|
||||
non-distro binaries in `/usr/local` (default without `-Dprefix`) or `/opt`.
|
||||
|
||||
## Configuration options
|
||||
|
||||
### udev
|
||||
|
||||
The configuration system should automatically detect the udev rules directory,
|
||||
but you can override it by using the following argument to `configure`:
|
||||
but you can override it by using the following argument to `meson setup`:
|
||||
|
||||
```
|
||||
--with-udevrulesdir=/path/to/udev/rules.d
|
||||
-Dudev-rules-dir=/path/to/udev/rules.d
|
||||
```
|
||||
|
||||
### Input group
|
||||
|
||||
The udev rules that are installed provide read/write access to members of the
|
||||
input devices group. This defaults to `plugdev`, but can be modified using
|
||||
the following argument to `configure`:
|
||||
the following argument to `meson setup`:
|
||||
|
||||
```
|
||||
--with-input-group=group
|
||||
-Dinput-group=group
|
||||
```
|
||||
|
||||
### Systemd support
|
||||
|
|
@ -88,13 +85,13 @@ itself to run in the background. Typical deployments with systemd will have it
|
|||
run in the foreground, and disable timestamps in the logs, since those are
|
||||
inserted automatically by journald.
|
||||
|
||||
Systemd support is enabled by default, but can be disabled with the
|
||||
`--disable-systemd` argument to `configure`
|
||||
Systemd support is enabled by default, which disables timestamps in the program
|
||||
logs, but you can re-enable timestamps by passing `-Dsystemd-logs=disabled`
|
||||
argument to `meson setup`
|
||||
|
||||
It is also possible to configure the directory in which the service file is
|
||||
installed with the following option. This is ignored if you have specified
|
||||
`--disable-systemd`.
|
||||
installed with the following option. This is ignored if systemd is not found.
|
||||
|
||||
```
|
||||
--with-systemdsystemunitdir=/path/to/systemd/system
|
||||
-Dsystemd-unit-dir=/path/to/systemd/system
|
||||
```
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@
|
|||
ACLOCAL_AMFLAGS = -I m4
|
||||
|
||||
# Build any support libraries first
|
||||
SUBDIRS = lib
|
||||
SUBDIRS = subprojects
|
||||
|
||||
if USE_NLS
|
||||
SUBDIRS += po
|
||||
|
|
@ -38,7 +38,7 @@ BUILT_SOURCES += version-info.h
|
|||
CLEANFILES += version-info.h
|
||||
|
||||
version-info.h: ${top_srcdir}/version-info
|
||||
${top_srcdir}/version-info ${top_srcdir} >$@
|
||||
CC=${CC} ${top_srcdir}/version-info ${top_srcdir} >$@
|
||||
|
||||
########################################################################
|
||||
# Include automake stubs
|
||||
|
|
|
|||
|
|
@ -0,0 +1,58 @@
|
|||
# Privacy
|
||||
|
||||
This document describes how the libx52 project handles data, for compliance with
|
||||
privacy laws such as the GDPR and CCPA.
|
||||
|
||||
## Summary
|
||||
|
||||
- **No automatic collection**: The software does not transmit any data to the
|
||||
project or third parties. No telemetry, analytics, or crash reporting is
|
||||
implemented.
|
||||
- **Local operation**: All processing is on your machine. Configuration and logs
|
||||
stay local unless you choose to share them (e.g. when opening a bug report).
|
||||
|
||||
## Data that may be displayed or stored locally
|
||||
|
||||
| Data | Where | Purpose |
|
||||
|------|--------|---------|
|
||||
| **Device serial number** | `evtest` only | Shown when you run evtest against a connected device (for local identification). **Not** included in **x52bugreport** output. |
|
||||
| **Device type info** | `x52bugreport`, `evtest` | Vendor ID, product ID, device version, manufacturer and product name (e.g. "Saitek" / "X52 Pro"). No serial number or hostname in bugreport. |
|
||||
| **System information** | `x52bugreport` only | Kernel name/release, machine architecture, kernel version string. **Hostname is not included.** |
|
||||
| **Build environment** | `x52bugreport` only | Compiler, build date, kernel/arch/OS version at build time. No hostname or other machine identifier. |
|
||||
| **Paths** | Daemon logs (if enabled) | Log file path, config path, socket path. Default paths use system directories (e.g. `/var`, `/run`, `/etc`), not your home directory. |
|
||||
| **Configuration** | Config files (e.g. under `/etc/x52d/`) | MFD/LED and daemon settings. Stored only on your system. |
|
||||
|
||||
None of this data is sent anywhere by the software. The only way it leaves your
|
||||
system is if you voluntarily paste it (e.g. into a GitHub issue).
|
||||
|
||||
## Bug reports
|
||||
|
||||
**x52bugreport** output is designed to avoid personal and device identifiers:
|
||||
|
||||
- **Device serial number** and **system hostname** are **not** included in the output.
|
||||
- Included: package/build version, compiler, build date, kernel and machine type, library versions, device vendor/product ID and device name (manufacturer/product strings only).
|
||||
|
||||
You may still redact any line before posting if you prefer. For most bugs, the information above is sufficient.
|
||||
|
||||
## Your rights (GDPR / CCPA style)
|
||||
|
||||
- **No account or sign-up** is required to use the software, so we do not hold
|
||||
an account-based profile on you.
|
||||
- **No selling of data**: We do not collect or sell personal data.
|
||||
- **Transparency**: This document describes what the software can display or
|
||||
store locally.
|
||||
- **Control**: You decide whether to run `x52bugreport` and what to include when
|
||||
opening an issue.
|
||||
|
||||
## Third-party services
|
||||
|
||||
- **Source and issues**: If you clone the repo or open an issue on GitHub,
|
||||
GitHub’s privacy policy applies to that interaction ([GitHub Privacy
|
||||
Statement](https://docs.github.com/en/site-policy/privacy-policies/github-privacy-statement)).
|
||||
- **Packages**: Installation via Ubuntu PPA or Arch AUR is subject to
|
||||
Canonical’s or the AUR’s respective terms and privacy practices.
|
||||
|
||||
## Changes
|
||||
|
||||
We may update this document to reflect changes in the software or in legal
|
||||
requirements. The current version is in the project repository.
|
||||
|
|
@ -4,6 +4,8 @@ Saitek X52Pro joystick driver for Linux
|
|||

|
||||

|
||||

|
||||
[](https://sonarcloud.io/summary/new_code?id=nirenjan_libx52)
|
||||
[](https://codecov.io/gh/nirenjan/libx52)
|
||||
|
||||
This project adds a new driver for the Saitek/MadCatz X52 Pro flight
|
||||
control system. The X52 pro is a HOTAS (hand on throttle and stick)
|
||||
|
|
@ -18,6 +20,8 @@ capable of reading the joystick, but it cannot control the MFD or LEDs.
|
|||
Most of the extra functionality can be handled from userspace. See
|
||||
the individual folders for README information.
|
||||
|
||||
For data handling and privacy (e.g. GDPR/CCPA), see [PRIVACY.md](PRIVACY.md).
|
||||
|
||||
**Note:** This repository currently only provides commandline interfaces to
|
||||
control the MFD and LEDs. If you are not comfortable working in the commandline,
|
||||
then the [gx52](https://gitlab.com/leinardi/gx52) project might be a better fit
|
||||
|
|
|
|||
|
|
@ -0,0 +1,9 @@
|
|||
# Security Policy
|
||||
|
||||
## Reporting Security Vulnerabiltiies
|
||||
|
||||
You may report a security vulnerability by [creating a new security advisory](https://github.com/nirenjan/libx52/security/advisories/new).
|
||||
|
||||
## Supported Versions
|
||||
|
||||
All security fixes will be made to the `master` branch. Older versions are not supported.
|
||||
|
|
@ -21,4 +21,6 @@ x52bugreport_LDFLAGS = \
|
|||
|
||||
x52bugreport_LDADD = libx52io.la
|
||||
|
||||
$(x52bugreport_OBJECTS): version-info.h
|
||||
|
||||
EXTRA_DIST += bugreport/bugreport.dox
|
||||
|
|
|
|||
|
|
@ -59,15 +59,13 @@ static void print_devinfo(void)
|
|||
printf("Device name: '%s' '%s'\n",
|
||||
libx52io_get_manufacturer_string(ctx),
|
||||
libx52io_get_product_string(ctx));
|
||||
printf("Serial number: '%s'\n",
|
||||
libx52io_get_serial_number_string(ctx));
|
||||
|
||||
libx52io_close(ctx);
|
||||
devinfo_cleanup:
|
||||
libx52io_exit(ctx);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
int main(void)
|
||||
{
|
||||
const struct libusb_version *libusb;
|
||||
|
||||
|
|
@ -75,7 +73,12 @@ int main(int argc, char **argv)
|
|||
puts("================");
|
||||
printf("Package version: %s\n", VERSION);
|
||||
printf("Build version: %s\n", BUILD_VERSION);
|
||||
printf("Built on: %s\n", BUILD_DATE);
|
||||
printf("Build host kernel: %s\n", BUILD_KERNEL);
|
||||
printf("Build host architecture: %s\n", BUILD_ARCH);
|
||||
printf("Build host version: %s\n", BUILD_OS_VERSION);
|
||||
printf("Build target: %s\n", BUILD_TARGET);
|
||||
printf("Compiler: %s\n", BUILD_COMPILER);
|
||||
printf("Build date: %s\n", BUILD_DATE);
|
||||
printf("version-info %s\n", BUILD_VERSION_INFO_IDENT);
|
||||
|
||||
puts("");
|
||||
|
|
|
|||
|
|
@ -15,6 +15,10 @@
|
|||
current system and build environment. The reported information can be provided
|
||||
when raising a bug report on https://github.com/nirenjan/libx52/issues.
|
||||
|
||||
The output does not include device serial number or system hostname. You may
|
||||
redact any line before posting if you prefer. See the project PRIVACY.md for
|
||||
details.
|
||||
|
||||
# USAGE
|
||||
|
||||
\b x52bugreport
|
||||
|
|
|
|||
|
|
@ -0,0 +1,66 @@
|
|||
#######################################################################
|
||||
# Version information
|
||||
#######################################################################
|
||||
compiler_version = run_command(compiler.cmd_array(), '--version',
|
||||
capture: true,
|
||||
check: true).stdout().split('\n')[0]
|
||||
|
||||
build_date = run_command('date', '+%Y-%m-%dT%H:%M:%S%z',
|
||||
capture: true,
|
||||
check: true).stdout().strip()
|
||||
|
||||
build_kernel = run_command('uname', '-sr',
|
||||
capture: true,
|
||||
check: true).stdout().strip()
|
||||
|
||||
build_arch = run_command('uname', '-mp',
|
||||
capture: true,
|
||||
check: true).stdout().strip()
|
||||
|
||||
build_os_version = run_command('uname', '-v',
|
||||
capture: true,
|
||||
check: true).stdout().strip()
|
||||
|
||||
built_for = '@0@ @1@ @2@-endian'.format(
|
||||
host_machine.system(),
|
||||
host_machine.cpu(),
|
||||
host_machine.endian(),
|
||||
)
|
||||
|
||||
git = find_program('git', required: false)
|
||||
if git.found()
|
||||
vcs_describe = run_command(git, 'describe', '--dirty',
|
||||
capture: true,
|
||||
check: false).stdout().strip()
|
||||
if vcs_describe == ''
|
||||
vcs_describe = meson.project_version()
|
||||
endif
|
||||
else
|
||||
vcs_describe = meson.project_version()
|
||||
endif
|
||||
|
||||
version_data = configuration_data()
|
||||
version_data.set_quoted('BUILD_VERSION', vcs_describe)
|
||||
version_data.set_quoted('BUILD_DATE', build_date)
|
||||
version_data.set_quoted('BUILD_KERNEL', build_kernel)
|
||||
version_data.set_quoted('BUILD_ARCH', build_arch)
|
||||
version_data.set_quoted('BUILD_OS_VERSION', build_os_version)
|
||||
version_data.set_quoted('BUILD_COMPILER', compiler_version)
|
||||
version_data.set_quoted('BUILD_TARGET', built_for)
|
||||
version_data.set_quoted('BUILD_VERSION_INFO_IDENT', '$Id$')
|
||||
|
||||
version_info_h = configure_file(
|
||||
input: 'version-info.h.meson',
|
||||
output: 'version-info.h',
|
||||
configuration: version_data
|
||||
)
|
||||
|
||||
# x52bugreport
|
||||
exe_bugreport = executable('x52bugreport', 'bugreport.c',
|
||||
install: true,
|
||||
include_directories: [includes],
|
||||
dependencies: [dep_libusb, dep_hidapi],
|
||||
link_with: [lib_libx52io])
|
||||
|
||||
# Test only to get code coverage
|
||||
test('x52bugreport', exe_bugreport, protocol:'exitcode')
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
#mesondefine BUILD_VERSION
|
||||
#mesondefine BUILD_DATE
|
||||
#mesondefine BUILD_KERNEL
|
||||
#mesondefine BUILD_ARCH
|
||||
#mesondefine BUILD_OS_VERSION
|
||||
#mesondefine BUILD_COMPILER
|
||||
#mesondefine BUILD_TARGET
|
||||
#mesondefine BUILD_VERSION_INFO_IDENT
|
||||
|
|
@ -17,7 +17,7 @@ TESTS += test-cli
|
|||
check_PROGRAMS += test-cli
|
||||
|
||||
test_cli_SOURCES = cli/x52_cli.c cli/test_x52_cli.c
|
||||
test_cli_CFLAGS = -DX52_CLI_TESTING -I $(top_srcdir)/libx52
|
||||
test_cli_CFLAGS = @CMOCKA_CFLAGS@ -DX52_CLI_TESTING -I $(top_srcdir)/libx52
|
||||
test_cli_LDFLAGS = @CMOCKA_LIBS@ $(WARN_LDFLAGS)
|
||||
|
||||
# Add a dependency on test_x52_cli_tests.c
|
||||
|
|
|
|||
|
|
@ -0,0 +1,16 @@
|
|||
# x52cli
|
||||
executable('x52cli', 'x52_cli.c',
|
||||
install: true,
|
||||
include_directories: [includes],
|
||||
link_with: lib_libx52)
|
||||
|
||||
test_cli = executable('test-cli', 'x52_cli.c', 'test_x52_cli.c',
|
||||
build_by_default: false,
|
||||
c_args: ['-DX52_CLI_TESTING'],
|
||||
include_directories: [includes],
|
||||
dependencies: [dep_cmocka],
|
||||
)
|
||||
|
||||
test('test-cli', test_cli, protocol: 'tap')
|
||||
|
||||
|
||||
|
|
@ -26,22 +26,26 @@ int libx52_init(libx52_device **dev)
|
|||
|
||||
int libx52_connect(libx52_device *dev)
|
||||
{
|
||||
(void)dev;
|
||||
function_called();
|
||||
return mock();
|
||||
}
|
||||
|
||||
int libx52_update(libx52_device *dev)
|
||||
{
|
||||
(void)dev;
|
||||
return LIBX52_SUCCESS;
|
||||
}
|
||||
|
||||
void libx52_exit(libx52_device *dev)
|
||||
{
|
||||
(void)dev;
|
||||
return;
|
||||
}
|
||||
|
||||
const char *libx52_strerror(libx52_error_code rc)
|
||||
{
|
||||
(void)rc;
|
||||
function_called();
|
||||
return "";
|
||||
}
|
||||
|
|
@ -172,7 +176,7 @@ const struct CMUnitTest tests[] = {
|
|||
#include "test_x52_cli_tests.c"
|
||||
};
|
||||
|
||||
int main(int argc, char **argv)
|
||||
int main(void)
|
||||
{
|
||||
cmocka_set_message_output(CM_OUTPUT_TAP);
|
||||
cmocka_run_group_tests(tests, NULL, NULL);
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
|
||||
#ifndef TEST_LIST
|
||||
// Setup the test case function
|
||||
#define TEST_CASE(tc) static void tc(void **state)
|
||||
#define TEST_CASE(tc) static void tc(void **state __attribute__((unused)))
|
||||
#define TEST_DEF(x) x
|
||||
// Function header, this calls the corresponding libx52 function, and expects
|
||||
// a certain number of calls to that function
|
||||
|
|
|
|||
|
|
@ -462,12 +462,12 @@ static void do_help(const struct command_handler *cmd)
|
|||
if (cmd) {
|
||||
fprintf(stderr, "Command usage: %s\n", cmd->help);
|
||||
} else {
|
||||
printf("\nCommands:\n");
|
||||
fprintf(stderr, "\nCommands:\n");
|
||||
for (i = 0; i < X52_CTL_CMD_MAX; i++) {
|
||||
printf("\t%s\n", handlers[i].help);
|
||||
fprintf(stderr, "\t%s\n", handlers[i].help);
|
||||
}
|
||||
|
||||
printf("\nWARNING: raw command may damage your device\n\n");
|
||||
fprintf(stderr, "\nWARNING: raw command may damage your device\n\n");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,39 @@
|
|||
/* Define to 1 if translation of program messages to the user's native
|
||||
language is requested. */
|
||||
#mesondefine ENABLE_NLS
|
||||
|
||||
/* Define to 1 if the system has the `noreturn' function attribute */
|
||||
#mesondefine HAVE_FUNC_ATTRIBUTE_NORETURN
|
||||
|
||||
/* Define to 1 if `tm_gmtoff' is a member of `struct tm'. */
|
||||
#mesondefine HAVE_STRUCT_TM_TM_GMTOFF
|
||||
|
||||
/* Name of package */
|
||||
#mesondefine PACKAGE
|
||||
|
||||
/* Define to the address where bug reports for this package should be sent. */
|
||||
#mesondefine PACKAGE_BUGREPORT
|
||||
|
||||
/* Define to the full name of this package. */
|
||||
#mesondefine PACKAGE_NAME
|
||||
|
||||
/* Define to the version of this package. */
|
||||
#mesondefine PACKAGE_VERSION
|
||||
|
||||
/* Define to the version of this package. */
|
||||
#mesondefine VERSION
|
||||
|
||||
/* Define to the directory where locale files are stored */
|
||||
#mesondefine LOCALEDIR
|
||||
|
||||
/* Define to the location of the configuration directory */
|
||||
#mesondefine SYSCONFDIR
|
||||
|
||||
/* Define to the location of the local state directory */
|
||||
#mesondefine LOCALSTATEDIR
|
||||
|
||||
/* Define to the location of the log directory */
|
||||
#define LOGDIR LOCALSTATEDIR "/log"
|
||||
|
||||
/* Define to the location of the run directory */
|
||||
#define RUNDIR LOCALSTATEDIR "/run"
|
||||
|
|
@ -81,6 +81,9 @@ AM_COND_IF([LINUX], [hidapi_backend=hidapi-hidraw], [hidapi_backend=hidapi])
|
|||
PKG_CHECK_MODULES([HIDAPI], [${hidapi_backend}])
|
||||
AC_SUBST([HIDAPI_PC], [${hidapi_backend}])
|
||||
|
||||
# Check for inih library, this is now packaged with recent distros
|
||||
PKG_CHECK_MODULES([INIH], [inih])
|
||||
|
||||
# Check for pthreads
|
||||
ACX_PTHREAD
|
||||
|
||||
|
|
@ -135,12 +138,11 @@ AC_CONFIG_HEADERS([config.h])
|
|||
|
||||
AC_CONFIG_FILES([ po/Makefile.in
|
||||
Makefile
|
||||
lib/Makefile
|
||||
subprojects/Makefile
|
||||
libx52/libx52.pc
|
||||
libx52io/libx52io.pc
|
||||
libx52util/libx52util.pc
|
||||
lib/pinelog/Makefile
|
||||
lib/inih/Makefile
|
||||
subprojects/pinelog/Makefile
|
||||
udev/60-saitek-x52-x52pro.rules
|
||||
])
|
||||
AC_OUTPUT
|
||||
|
|
|
|||
|
|
@ -26,18 +26,16 @@ x52d_CFLAGS = \
|
|||
-I $(top_srcdir)/libx52io \
|
||||
-I $(top_srcdir)/libx52 \
|
||||
-I $(top_srcdir)/libx52util \
|
||||
-I $(top_srcdir)/lib/pinelog \
|
||||
-I $(top_srcdir)/lib/inih \
|
||||
-I $(top_srcdir)/subprojects/pinelog \
|
||||
-DSYSCONFDIR=\"$(sysconfdir)\" \
|
||||
-DLOCALEDIR=\"$(localedir)\" \
|
||||
-DLOGDIR=\"$(localstatedir)/log\" \
|
||||
-DRUNDIR=\"$(localstatedir)/run\" \
|
||||
@PTHREAD_CFLAGS@ $(WARN_CFLAGS)
|
||||
@INIH_CFLAGS@ @PTHREAD_CFLAGS@ $(WARN_CFLAGS)
|
||||
|
||||
x52d_LDFLAGS = @PTHREAD_LIBS@ $(WARN_LDFLAGS)
|
||||
x52d_LDFLAGS = @INIH_LIBS@ @PTHREAD_LIBS@ $(WARN_LDFLAGS)
|
||||
x52d_LDADD = \
|
||||
lib/pinelog/libpinelog.la \
|
||||
lib/inih/libinih.la \
|
||||
subprojects/pinelog/libpinelog.la \
|
||||
libx52.la \
|
||||
@LTLIBINTL@
|
||||
|
||||
|
|
@ -112,6 +110,9 @@ EXTRA_DIST += \
|
|||
daemon/tests/logging/module.tc \
|
||||
daemon/tests/cli.tc
|
||||
|
||||
TESTS += daemon/test_daemon_comm.py
|
||||
|
||||
if HAVE_CMOCKA
|
||||
check_PROGRAMS += x52d-mouse-test
|
||||
|
||||
x52d_mouse_test_SOURCES = \
|
||||
|
|
@ -122,16 +123,15 @@ x52d_mouse_test_CFLAGS = \
|
|||
-I $(top_srcdir) \
|
||||
-I $(top_srcdir)/libx52 \
|
||||
-I $(top_srcdir)/libx52io \
|
||||
-I $(top_srcdir)/lib/pinelog \
|
||||
$(WARN_CFLAGS)
|
||||
-I $(top_srcdir)/subprojects/pinelog \
|
||||
$(WARN_CFLAGS) @CMOCKA_CFLAGS@
|
||||
x52d_mouse_test_LDFLAGS = @CMOCKA_LIBS@ $(WARN_LDFLAGS)
|
||||
x52d_mouse_test_LDADD = \
|
||||
lib/pinelog/libpinelog.la \
|
||||
subprojects/pinelog/libpinelog.la \
|
||||
@LTLIBINTL@
|
||||
|
||||
TESTS += \
|
||||
daemon/test_daemon_comm.py \
|
||||
x52d-mouse-test
|
||||
TESTS += x52d-mouse-test
|
||||
endif
|
||||
|
||||
if HAVE_SYSTEMD
|
||||
if !IS_MAKE_DISTCHECK
|
||||
|
|
|
|||
|
|
@ -0,0 +1,89 @@
|
|||
# x52d
|
||||
libx52dcomm_version = '1.0.0'
|
||||
|
||||
libx52dcomm_sources = [
|
||||
'x52d_comm_client.c',
|
||||
'x52d_comm_internal.c'
|
||||
]
|
||||
|
||||
install_headers('x52dcomm.h', subdir: meson.project_name())
|
||||
|
||||
lib_libx52dcomm = library('x52dcomm', libx52dcomm_sources,
|
||||
dependencies: [dep_intl],
|
||||
version: libx52dcomm_version,
|
||||
install: true,
|
||||
include_directories: includes)
|
||||
|
||||
x52d_sources = [
|
||||
'x52d_main.c',
|
||||
'x52d_config_parser.c',
|
||||
'x52d_config_dump.c',
|
||||
'x52d_config.c',
|
||||
'x52d_device.c',
|
||||
'x52d_client.c',
|
||||
'x52d_clock.c',
|
||||
'x52d_mouse.c',
|
||||
'x52d_notify.c',
|
||||
'x52d_led.c',
|
||||
'x52d_command.c',
|
||||
]
|
||||
|
||||
dep_threads = dependency('threads')
|
||||
x52d_linkwith = [lib_libx52, lib_libx52dcomm]
|
||||
x52d_deps = [dep_pinelog, dep_inih, dep_threads, dep_intl]
|
||||
x52d_cflags = []
|
||||
if dep_evdev.found()
|
||||
x52d_sources += 'x52d_io.c'
|
||||
x52d_sources += 'x52d_mouse_evdev.c'
|
||||
x52d_cflags += '-DHAVE_EVDEV'
|
||||
x52d_linkwith += lib_libx52io
|
||||
x52d_deps += dep_evdev
|
||||
endif
|
||||
|
||||
exe_x52d = executable('x52d', x52d_sources,
|
||||
install: true,
|
||||
include_directories: includes,
|
||||
c_args: x52d_cflags,
|
||||
dependencies: x52d_deps,
|
||||
link_with: x52d_linkwith)
|
||||
|
||||
exe_x52ctl = executable('x52ctl', 'x52ctl.c',
|
||||
install: true,
|
||||
dependencies: [dep_intl],
|
||||
include_directories: includes,
|
||||
link_with: lib_libx52dcomm)
|
||||
|
||||
install_data('x52d.conf',
|
||||
install_dir: join_paths(get_option('sysconfdir'), 'x52d'))
|
||||
|
||||
test('daemon-communication', files('test_daemon_comm.py')[0],
|
||||
depends: [exe_x52d, exe_x52ctl], protocol: 'tap')
|
||||
|
||||
x52d_mouse_test_sources = ['x52d_mouse_test.c', 'x52d_mouse.c']
|
||||
x52d_mouse_test = executable('x52d-mouse-test', x52d_mouse_test_sources,
|
||||
include_directories: includes,
|
||||
dependencies: [dep_pinelog, dep_cmocka, dep_intl])
|
||||
|
||||
test('x52d-mouse-test', x52d_mouse_test, protocol: 'tap')
|
||||
|
||||
# Install service file
|
||||
if dep_systemd.found()
|
||||
systemd_system_unit_dir = get_option('systemd-unit-dir')
|
||||
if systemd_system_unit_dir == ''
|
||||
systemd_system_unit_dir = dep_systemd.get_variable(
|
||||
pkgconfig: 'systemd_system_unit_dir',
|
||||
default_value: '/lib/systemd/system')
|
||||
endif
|
||||
sed = find_program('sed')
|
||||
bindir_path = get_option('prefix') / get_option('bindir')
|
||||
sed_script = 's|%bindir%|' + bindir_path + '|g'
|
||||
|
||||
systemd_service_file = configure_file(
|
||||
input: 'x52d.service.in',
|
||||
output: 'x52d.service',
|
||||
command: [sed, sed_script, '@INPUT@'],
|
||||
capture: true,
|
||||
install: true,
|
||||
install_dir: systemd_system_unit_dir
|
||||
)
|
||||
endif
|
||||
|
|
@ -193,10 +193,9 @@ A side effect of this is that the client could request a set for any arbitrary
|
|||
section and key pair, and if that pair was not recognized, it would be ignored,
|
||||
but the daemon would still send an `OK` response.
|
||||
|
||||
Finally, this will only set the value within the configuration memory
|
||||
structures, and will not invoke any callback to update the rest of the threads
|
||||
or device state. The client will need to call the `apply` subcommand to actually
|
||||
invoke the necessary callbacks.
|
||||
This will set the value within the configuration memory structures, and will
|
||||
immediately invoke the relevant callback to update the rest of the threads or
|
||||
device state.
|
||||
|
||||
\b Arguments
|
||||
|
||||
|
|
@ -221,22 +220,6 @@ invoke the necessary callbacks.
|
|||
ERR\0Error 22 setting 'led.fire'='none': Invalid argument\0
|
||||
```
|
||||
|
||||
# Apply configuration
|
||||
|
||||
The `config apply` command will invoke all the callbacks and ensure that the
|
||||
configuration is applied to the running state.
|
||||
|
||||
\b Arguments
|
||||
|
||||
- `config`
|
||||
- `apply`
|
||||
|
||||
\b Returns
|
||||
|
||||
- `OK`
|
||||
- `config`
|
||||
- `apply`
|
||||
|
||||
*/
|
||||
|
||||
/**
|
||||
|
|
@ -259,12 +242,14 @@ user to fine tune the logging while the daemon is running.
|
|||
of modules is below:
|
||||
|
||||
- \c Config
|
||||
- \c Cllient
|
||||
- \c Clock
|
||||
- \c Command
|
||||
- \c Device
|
||||
- \c IO
|
||||
- \c LED
|
||||
- \c Mouse
|
||||
- \c Notify
|
||||
|
||||
# Logging levels
|
||||
|
||||
|
|
|
|||
|
|
@ -89,15 +89,53 @@ static int send_command(int sock_fd, int argc, char **argv)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void interactive_mode(int sock_fd)
|
||||
{
|
||||
bool keep_running = true;
|
||||
char buffer[1024];
|
||||
|
||||
fputs("> ", stdout);
|
||||
while (keep_running && fgets(buffer, sizeof(buffer), stdin) != NULL) {
|
||||
int sargc;
|
||||
char *sargv[512] = { 0 };
|
||||
int pos;
|
||||
|
||||
if (strcasecmp(buffer, "quit\n") == 0) {
|
||||
keep_running = false;
|
||||
} else {
|
||||
/* Break the buffer into argc/argv */
|
||||
sargc = 0;
|
||||
pos = 0;
|
||||
while (buffer[pos]) {
|
||||
if (isspace(buffer[pos])) {
|
||||
buffer[pos] = '\0';
|
||||
pos++;
|
||||
} else {
|
||||
sargv[sargc] = &buffer[pos];
|
||||
sargc++;
|
||||
for (; buffer[pos] && !isspace(buffer[pos]); pos++);
|
||||
}
|
||||
}
|
||||
|
||||
if (send_command(sock_fd, sargc, sargv)) {
|
||||
keep_running = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (keep_running) {
|
||||
fputs("\n> ", stdout);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
bool interactive = false;
|
||||
char *socket_path = NULL;
|
||||
const char *socket_path = NULL;
|
||||
int opt;
|
||||
int sock_fd;
|
||||
int rc = EXIT_SUCCESS;
|
||||
|
||||
char buffer[1024];
|
||||
|
||||
/*
|
||||
* Parse command line arguments
|
||||
|
|
@ -142,47 +180,13 @@ int main(int argc, char **argv)
|
|||
_("Running in interactive mode, ignoring extra arguments\n"));
|
||||
}
|
||||
|
||||
fputs("> ", stdout);
|
||||
while (fgets(buffer, sizeof(buffer), stdin) != NULL) {
|
||||
int sargc;
|
||||
char *sargv[512] = { 0 };
|
||||
int pos;
|
||||
|
||||
if (strcasecmp(buffer, "quit\n") == 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
/* Break the buffer into argc/argv */
|
||||
sargc = 0;
|
||||
pos = 0;
|
||||
while (buffer[pos]) {
|
||||
if (isspace(buffer[pos])) {
|
||||
buffer[pos] = '\0';
|
||||
pos++;
|
||||
} else {
|
||||
sargv[sargc] = &buffer[pos];
|
||||
sargc++;
|
||||
for (; buffer[pos] && !isspace(buffer[pos]); pos++);
|
||||
}
|
||||
}
|
||||
|
||||
if (send_command(sock_fd, sargc, sargv)) {
|
||||
rc = EXIT_FAILURE;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
fputs("\n> ", stdout);
|
||||
}
|
||||
|
||||
interactive_mode(sock_fd);
|
||||
} else {
|
||||
if (send_command(sock_fd, argc - optind, &argv[optind])) {
|
||||
rc = EXIT_FAILURE;
|
||||
goto cleanup;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
cleanup:
|
||||
close(sock_fd);
|
||||
return rc;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,6 +27,11 @@ bool x52d_client_register(int client_fd[X52D_MAX_CLIENTS], int sock_fd)
|
|||
int fd;
|
||||
int i;
|
||||
|
||||
#if defined(__GNUC__) && !defined(__clang__) && __GNUC__ >= 13
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wanalyzer-fd-leak"
|
||||
#endif
|
||||
|
||||
fd = accept(sock_fd, NULL, NULL);
|
||||
if (fd < 0) {
|
||||
PINELOG_ERROR(_("Error accepting client connection on socket fd %d: %s"),
|
||||
|
|
@ -40,6 +45,11 @@ bool x52d_client_register(int client_fd[X52D_MAX_CLIENTS], int sock_fd)
|
|||
goto error;
|
||||
}
|
||||
|
||||
#if defined(__GNUC__) && !defined(__clang__) && __GNUC__ >= 13
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
|
||||
|
||||
for (i = 0; i < X52D_MAX_CLIENTS; i++) {
|
||||
if (client_fd[i] == INVALID_CLIENT) {
|
||||
PINELOG_TRACE("Accepted client %d on socket %d, slot %d", fd, sock_fd, i);
|
||||
|
|
@ -109,12 +119,11 @@ int x52d_client_poll(int client_fd[X52D_MAX_CLIENTS], struct pollfd pfd[MAX_CONN
|
|||
|
||||
PINELOG_TRACE("Polling %d file descriptors", pfd_count);
|
||||
|
||||
retry_poll:
|
||||
rc = poll(pfd, pfd_count, -1);
|
||||
do {
|
||||
rc = poll(pfd, pfd_count, -1);
|
||||
} while (rc < 0 && errno == EINTR);
|
||||
|
||||
if (rc < 0) {
|
||||
if (errno == EINTR) {
|
||||
goto retry_poll;
|
||||
}
|
||||
PINELOG_ERROR(_("Error %d when polling %d descriptors: %s"),
|
||||
errno, pfd_count, strerror(errno));
|
||||
} else if (rc == 0) {
|
||||
|
|
|
|||
|
|
@ -97,8 +97,11 @@ cleanup:
|
|||
if (orig_tz == NULL) {
|
||||
unsetenv("TZ");
|
||||
} else {
|
||||
setenv("TZ", orig_tz_copy, true);
|
||||
free(orig_tz_copy);
|
||||
// If the copy is NULL, then we didn't change TZ, so don't bother
|
||||
if (orig_tz_copy != NULL) {
|
||||
setenv("TZ", orig_tz_copy, true);
|
||||
free(orig_tz_copy);
|
||||
}
|
||||
}
|
||||
|
||||
if (new_tz != NULL) {
|
||||
|
|
@ -166,6 +169,7 @@ static pthread_t clock_thr;
|
|||
static void * x52_clock_thr(void *param)
|
||||
{
|
||||
int rc;
|
||||
(void)param;
|
||||
|
||||
PINELOG_INFO(_("Starting X52 clock manager thread"));
|
||||
for (;;) {
|
||||
|
|
|
|||
|
|
@ -89,6 +89,22 @@ sock_failure:
|
|||
return -1;
|
||||
}
|
||||
|
||||
int x52d_listen_socket(struct sockaddr_un *local, int len, int sock_fd)
|
||||
{
|
||||
/* Cleanup any existing socket */
|
||||
unlink(local->sun_path);
|
||||
if (bind(sock_fd, (struct sockaddr *)local, (socklen_t)len) < 0) {
|
||||
/* Failure binding socket */
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (listen(sock_fd, X52D_MAX_CLIENTS) < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void x52d_split_args(int *argc, char **argv, char *buffer, int buflen)
|
||||
{
|
||||
int i = 0;
|
||||
|
|
|
|||
|
|
@ -239,6 +239,19 @@ static const char *lmap_get_string(const struct level_map *map, int level)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
#define DATA_LMAP(map, level, resp) do {\
|
||||
int input_level_ ## __LINE__ = level; \
|
||||
const char *lmap_level_ ## __LINE__ = lmap_get_string(map, input_level_ ## __LINE__); \
|
||||
char lmap_unknown_level ## __LINE__[32] = {0}; \
|
||||
if (lmap_level_ ## __LINE__ == NULL) { \
|
||||
snprintf(lmap_unknown_level ## __LINE__, sizeof(lmap_unknown_level ## __LINE__), \
|
||||
"unknown (%d)", input_level_ ## __LINE__); \
|
||||
lmap_level_ ## __LINE__ = lmap_unknown_level ## __LINE__; \
|
||||
} \
|
||||
DATA(resp, lmap_level_ ## __LINE__); \
|
||||
} while(0)
|
||||
|
||||
|
||||
static int array_find_index(const char **array, int nmemb, const char *string)
|
||||
{
|
||||
int i;
|
||||
|
|
@ -287,14 +300,13 @@ static void cmd_logging(char *buffer, int *buflen, int argc, char **argv)
|
|||
// logging show [module]
|
||||
MATCH(1, "show") {
|
||||
if (argc == 2) {
|
||||
// Show default logging level
|
||||
DATA("global", lmap_get_string(loglevels, pinelog_get_level()));
|
||||
DATA_LMAP(loglevels, pinelog_get_level(), "global");
|
||||
} else if (argc == 3) {
|
||||
int module = array_find_index(modules, X52D_MOD_MAX, argv[2]);
|
||||
if (module == X52D_MOD_MAX) {
|
||||
ERR_fmt("Invalid module '%s'", argv[2]);
|
||||
} else {
|
||||
DATA(argv[2], lmap_get_string(loglevels, pinelog_get_module_level(module)));
|
||||
DATA_LMAP(loglevels, pinelog_get_module_level(module), argv[2]);
|
||||
}
|
||||
} else {
|
||||
ERR_fmt("Unexpected arguments for 'logging show' command; got %d, expected 2 or 3", argc);
|
||||
|
|
@ -397,6 +409,7 @@ int x52d_command_loop(int sock_fd)
|
|||
|
||||
static void * x52d_command_thread(void *param)
|
||||
{
|
||||
(void)param;
|
||||
for (;;) {
|
||||
if (x52d_command_loop(command_sock_fd) < 0) {
|
||||
PINELOG_FATAL(_("Error %d during command loop: %s"),
|
||||
|
|
@ -412,7 +425,6 @@ int x52d_command_init(const char *sock_path)
|
|||
int sock_fd;
|
||||
int len;
|
||||
struct sockaddr_un local;
|
||||
int flags;
|
||||
|
||||
x52d_client_init(client_fd);
|
||||
|
||||
|
|
@ -431,33 +443,17 @@ int x52d_command_init(const char *sock_path)
|
|||
return -1;
|
||||
}
|
||||
|
||||
/* Mark the socket as non-blocking */
|
||||
flags = fcntl(sock_fd, F_GETFL);
|
||||
if (flags < 0) {
|
||||
PINELOG_ERROR(_("Error getting command socket flags: %s"), strerror(errno));
|
||||
goto sock_failure;
|
||||
}
|
||||
if (fcntl(sock_fd, F_SETFL, flags | O_NONBLOCK) < 0) {
|
||||
PINELOG_ERROR(_("Error setting command socket flags: %s"), strerror(errno));
|
||||
goto sock_failure;
|
||||
}
|
||||
|
||||
/* Cleanup any existing socket */
|
||||
unlink(local.sun_path);
|
||||
if (bind(sock_fd, (struct sockaddr *)&local, (socklen_t)len) < 0) {
|
||||
/* Failure binding socket */
|
||||
PINELOG_ERROR(_("Error binding to command socket: %s"), strerror(errno));
|
||||
goto listen_failure;
|
||||
}
|
||||
|
||||
if (listen(sock_fd, X52D_MAX_CLIENTS) < 0) {
|
||||
PINELOG_ERROR(_("Error listening on command socket: %s"), strerror(errno));
|
||||
goto listen_failure;
|
||||
}
|
||||
|
||||
command_sock_fd = sock_fd;
|
||||
if (command_sock_fd < 0) {
|
||||
command_sock_fd = -1;
|
||||
|
||||
/* Mark the socket as non-blocking */
|
||||
if (x52d_set_socket_nonblocking(sock_fd) < 0) {
|
||||
PINELOG_ERROR(_("Error marking command socket as nonblocking: %s"),
|
||||
strerror(errno));
|
||||
goto sock_failure;
|
||||
}
|
||||
|
||||
if (x52d_listen_socket(&local, len, sock_fd) < 0) {
|
||||
PINELOG_ERROR(_("Error listening on command socket: %s"), strerror(errno));
|
||||
goto listen_failure;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ static volatile bool device_update_needed;
|
|||
static void *x52_dev_thr(void *param)
|
||||
{
|
||||
int rc;
|
||||
(void)param;
|
||||
|
||||
#define DEV_ACQ_DELAY 5 // seconds
|
||||
#define DEV_UPD_DELAY 50000 // microseconds
|
||||
|
|
|
|||
|
|
@ -36,6 +36,7 @@ static void *x52_io_thr(void *param)
|
|||
int rc;
|
||||
libx52io_report report;
|
||||
libx52io_report prev_report;
|
||||
(void)param;
|
||||
|
||||
#define IO_READ_TIMEOUT 50 /* milliseconds */
|
||||
#define IO_ACQ_TIMEOUT 5 /* seconds */
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@
|
|||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <signal.h>
|
||||
#include <inttypes.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
|
||||
|
|
@ -38,12 +39,14 @@ static void termination_handler(int signum)
|
|||
static volatile bool flag_reload;
|
||||
static void reload_handler(int signum)
|
||||
{
|
||||
(void)signum;
|
||||
flag_reload = true;
|
||||
}
|
||||
|
||||
static volatile bool flag_save_cfg;
|
||||
static void save_config_handler(int signum)
|
||||
{
|
||||
(void)signum;
|
||||
flag_save_cfg = true;
|
||||
}
|
||||
|
||||
|
|
@ -111,14 +114,16 @@ static void start_daemon(bool foreground, const char *pid_file)
|
|||
pid_fd = fopen(pid_file, "r");
|
||||
if (pid_fd != NULL) {
|
||||
int rc;
|
||||
intmax_t tmp_pid;
|
||||
|
||||
/* File exists, read the PID and check if it exists */
|
||||
rc = fscanf(pid_fd, "%u", &pid);
|
||||
rc = fscanf(pid_fd, "%" SCNdMAX, &tmp_pid);
|
||||
fclose(pid_fd);
|
||||
|
||||
if (rc != 1) {
|
||||
perror("fscanf");
|
||||
} else {
|
||||
pid = (pid_t)tmp_pid;
|
||||
rc = kill(pid, 0);
|
||||
if (rc == 0 || (rc < 0 && errno == EPERM)) {
|
||||
PINELOG_FATAL(_("Daemon is already running as PID %u"), pid);
|
||||
|
|
|
|||
|
|
@ -130,6 +130,7 @@ static void reset_reports(void)
|
|||
static void * x52_mouse_thr(void *param)
|
||||
{
|
||||
bool state_changed;
|
||||
(void)param;
|
||||
|
||||
PINELOG_INFO(_("Starting X52 virtual mouse driver thread"));
|
||||
for (;;) {
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@ void x52d_mouse_evdev_thread_control(bool enabled)
|
|||
|
||||
static void test_mouse_thread_enabled(void **state)
|
||||
{
|
||||
(void)state;
|
||||
#if defined HAVE_EVDEV
|
||||
expect_function_calls(x52d_mouse_evdev_thread_control, 1);
|
||||
expect_value(x52d_mouse_evdev_thread_control, enabled, true);
|
||||
|
|
@ -40,6 +41,7 @@ static void test_mouse_thread_enabled(void **state)
|
|||
|
||||
static void test_mouse_thread_disabled(void **state)
|
||||
{
|
||||
(void)state;
|
||||
#if defined HAVE_EVDEV
|
||||
expect_function_calls(x52d_mouse_evdev_thread_control, 1);
|
||||
expect_value(x52d_mouse_evdev_thread_control, enabled, false);
|
||||
|
|
@ -50,6 +52,7 @@ static void test_mouse_thread_disabled(void **state)
|
|||
|
||||
static void test_mouse_speed_negative(void **state)
|
||||
{
|
||||
(void)state;
|
||||
int orig_mouse_delay = mouse_delay;
|
||||
int orig_mouse_mult = mouse_mult;
|
||||
|
||||
|
|
@ -61,6 +64,7 @@ static void test_mouse_speed_negative(void **state)
|
|||
/* The following tests are dependent on the values in x52d_mouse.c */
|
||||
static void test_mouse_speed_0(void **state)
|
||||
{
|
||||
(void)state;
|
||||
x52d_cfg_set_Mouse_Speed(0);
|
||||
assert_int_equal(mouse_delay, 70000);
|
||||
assert_int_equal(mouse_mult, 4);
|
||||
|
|
@ -68,6 +72,7 @@ static void test_mouse_speed_0(void **state)
|
|||
|
||||
static void test_mouse_speed_mid_base(void **state)
|
||||
{
|
||||
(void)state;
|
||||
x52d_cfg_set_Mouse_Speed(6);
|
||||
assert_int_equal(mouse_delay, 40000);
|
||||
assert_int_equal(mouse_mult, 4);
|
||||
|
|
@ -75,6 +80,7 @@ static void test_mouse_speed_mid_base(void **state)
|
|||
|
||||
static void test_mouse_speed_max_base(void **state)
|
||||
{
|
||||
(void)state;
|
||||
x52d_cfg_set_Mouse_Speed(12);
|
||||
assert_int_equal(mouse_delay, 10000);
|
||||
assert_int_equal(mouse_mult, 4);
|
||||
|
|
@ -82,6 +88,7 @@ static void test_mouse_speed_max_base(void **state)
|
|||
|
||||
static void test_mouse_speed_min_hyper(void **state)
|
||||
{
|
||||
(void)state;
|
||||
x52d_cfg_set_Mouse_Speed(13);
|
||||
assert_int_equal(mouse_delay, 10000);
|
||||
assert_int_equal(mouse_mult, 5);
|
||||
|
|
@ -89,6 +96,7 @@ static void test_mouse_speed_min_hyper(void **state)
|
|||
|
||||
static void test_mouse_speed_mid_hyper(void **state)
|
||||
{
|
||||
(void)state;
|
||||
x52d_cfg_set_Mouse_Speed(22);
|
||||
assert_int_equal(mouse_delay, 10000);
|
||||
assert_int_equal(mouse_mult, 14);
|
||||
|
|
@ -96,6 +104,7 @@ static void test_mouse_speed_mid_hyper(void **state)
|
|||
|
||||
static void test_mouse_speed_max_hyper(void **state)
|
||||
{
|
||||
(void)state;
|
||||
x52d_cfg_set_Mouse_Speed(32);
|
||||
assert_int_equal(mouse_delay, 10000);
|
||||
assert_int_equal(mouse_mult, 24);
|
||||
|
|
@ -105,6 +114,7 @@ static void test_mouse_speed_above_max(void **state)
|
|||
{
|
||||
int orig_mouse_delay = mouse_delay;
|
||||
int orig_mouse_mult = mouse_mult;
|
||||
(void)state;
|
||||
|
||||
x52d_cfg_set_Mouse_Speed(33);
|
||||
assert_int_equal(mouse_delay, orig_mouse_delay);
|
||||
|
|
@ -113,12 +123,14 @@ static void test_mouse_speed_above_max(void **state)
|
|||
|
||||
static void test_mouse_reverse_scroll_enabled(void **state)
|
||||
{
|
||||
(void)state;
|
||||
x52d_cfg_set_Mouse_ReverseScroll(true);
|
||||
assert_int_equal(mouse_scroll_dir, -1);
|
||||
}
|
||||
|
||||
static void test_mouse_reverse_scroll_disabled(void **state)
|
||||
{
|
||||
(void)state;
|
||||
x52d_cfg_set_Mouse_ReverseScroll(false);
|
||||
assert_int_equal(mouse_scroll_dir, 1);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -52,15 +52,7 @@ static int listen_notify(const char *notify_sock_path)
|
|||
return -1;
|
||||
}
|
||||
|
||||
/* Cleanup any existing socket */
|
||||
unlink(local.sun_path);
|
||||
if (bind(sock_fd, (struct sockaddr *)&local, (socklen_t)len) < 0) {
|
||||
/* Failure binding socket */
|
||||
PINELOG_ERROR(_("Error binding to notification socket: %s"), strerror(errno));
|
||||
goto listen_failure;
|
||||
}
|
||||
|
||||
if (listen(sock_fd, X52D_MAX_CLIENTS) < 0) {
|
||||
if (x52d_listen_socket(&local, len, sock_fd) < 0) {
|
||||
PINELOG_ERROR(_("Error listening on notification socket: %s"), strerror(errno));
|
||||
goto listen_failure;
|
||||
}
|
||||
|
|
@ -79,47 +71,35 @@ static void * x52_notify_thr(void * param)
|
|||
char buffer[X52D_BUFSZ];
|
||||
uint16_t bufsiz;
|
||||
int rc;
|
||||
(void)param;
|
||||
|
||||
for (;;) {
|
||||
read_pipe_size:
|
||||
rc = read(notify_pipe[0], &bufsiz, sizeof(bufsiz));
|
||||
if (rc < 0) {
|
||||
if (errno == EINTR) {
|
||||
goto read_pipe_size;
|
||||
} else {
|
||||
PINELOG_ERROR(_("Error %d reading from pipe: %s"),
|
||||
errno, strerror(errno));
|
||||
}
|
||||
}
|
||||
|
||||
do {
|
||||
rc = read(notify_pipe[0], &bufsiz, sizeof(bufsiz));
|
||||
} while (rc < 0 && errno == EINTR);
|
||||
if (rc < 0) {
|
||||
PINELOG_ERROR(_("Error %d reading from pipe: %s"),
|
||||
errno, strerror(errno));
|
||||
// Error condition, try again
|
||||
continue;
|
||||
}
|
||||
|
||||
read_pipe_data:
|
||||
rc = read(notify_pipe[0], buffer, bufsiz);
|
||||
if (rc < 0) {
|
||||
if (errno == EINTR) {
|
||||
goto read_pipe_data;
|
||||
} else {
|
||||
PINELOG_ERROR(_("Error %d reading from pipe: %s"),
|
||||
errno, strerror(errno));
|
||||
}
|
||||
}
|
||||
|
||||
do {
|
||||
rc = read(notify_pipe[0], buffer, bufsiz);
|
||||
} while (rc < 0 && errno == EINTR);
|
||||
if (rc < 0) {
|
||||
PINELOG_ERROR(_("Error %d reading from pipe: %s"),
|
||||
errno, strerror(errno));
|
||||
// Error condition, try again
|
||||
continue;
|
||||
}
|
||||
|
||||
for (int i = 0; i < X52D_MAX_CLIENTS; i++) {
|
||||
// Broadcast to every connected client
|
||||
if (client_fd[i] != INVALID_CLIENT) {
|
||||
write_client_notification:
|
||||
rc = write(client_fd[i], buffer, bufsiz);
|
||||
if (rc < 0 && errno == EINTR) {
|
||||
goto write_client_notification;
|
||||
}
|
||||
do {
|
||||
rc = write(client_fd[i], buffer, bufsiz);
|
||||
} while (rc < 0 && errno == EINTR);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -167,6 +147,7 @@ static void * x52_notify_loop(void * param)
|
|||
{
|
||||
struct pollfd pfd[MAX_CONN];
|
||||
int rc;
|
||||
(void)param;
|
||||
|
||||
for (;;) {
|
||||
rc = x52d_client_poll(client_fd, pfd, notify_sock);
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ int x52d_setup_command_sock(const char *sock_path, struct sockaddr_un *remote);
|
|||
const char *x52d_notify_sock_path(const char *sock_path);
|
||||
int x52d_setup_notify_sock(const char *sock_path, struct sockaddr_un *remote);
|
||||
int x52d_set_socket_nonblocking(int sock_fd);
|
||||
int x52d_listen_socket(struct sockaddr_un *local, int len, int sock_fd);
|
||||
void x52d_split_args(int *argc, char **argv, char *buffer, int buflen);
|
||||
|
||||
#endif // !defined X52DCOMM_INTERNAL_H
|
||||
|
|
|
|||
|
|
@ -44,6 +44,7 @@ static bool exit_loop = false;
|
|||
|
||||
static void signal_handler(int sig)
|
||||
{
|
||||
(void)sig;
|
||||
exit_loop = true;
|
||||
}
|
||||
|
||||
|
|
@ -52,7 +53,7 @@ static bool denoise = true;
|
|||
|
||||
/* For i18n */
|
||||
#define _(x) gettext(x)
|
||||
int main(int argc, char **argv)
|
||||
int main(void)
|
||||
{
|
||||
libx52io_context *ctx;
|
||||
libx52io_report last, curr;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,6 @@
|
|||
# x52evtest
|
||||
executable('x52evtest', 'ev_test.c',
|
||||
install: true,
|
||||
include_directories: includes,
|
||||
dependencies: [dep_intl],
|
||||
link_with: [lib_libx52io])
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
#!/bin/sh
|
||||
# Install Doxygen HTML and man trees from the build directory.
|
||||
# Arguments are paths relative to the install prefix (MESON_INSTALL_DESTDIR_PREFIX).
|
||||
set -e
|
||||
|
||||
doc_html="$1"
|
||||
mandir="$2"
|
||||
|
||||
WANTED_PAGES="man1/x52cli.1 man1/x52bugreport.1"
|
||||
|
||||
if [ -d "$MESON_BUILD_ROOT/docs/html" ]; then
|
||||
mkdir -p "$MESON_INSTALL_DESTDIR_PREFIX/$doc_html"
|
||||
cp -R "$MESON_BUILD_ROOT/docs/html"/. "$MESON_INSTALL_DESTDIR_PREFIX/$doc_html/"
|
||||
fi
|
||||
|
||||
if [ -d "$MESON_BUILD_ROOT/docs/man" ]; then
|
||||
MANDIR="$MESON_INSTALL_DESTDIR_PREFIX/$mandir"
|
||||
mkdir -p "$MANDIR"
|
||||
for manpage in $WANTED_PAGES
|
||||
do
|
||||
section=$(dirname "$manpage")
|
||||
mkdir -p "$MANDIR/$section"
|
||||
cp "$MESON_BUILD_ROOT/docs/man/$manpage" "$MANDIR/$section"
|
||||
done
|
||||
fi
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
# x52test
|
||||
executable('x52test',
|
||||
'x52_test.c',
|
||||
'x52_test_mfd.c',
|
||||
'x52_test_led.c',
|
||||
'x52_test_clock.c',
|
||||
install: true,
|
||||
dependencies: [dep_intl],
|
||||
include_directories: includes,
|
||||
link_with: [lib_libx52])
|
||||
|
|
@ -1,27 +0,0 @@
|
|||
|
||||
The "inih" library is distributed under the New BSD license:
|
||||
|
||||
Copyright (c) 2009, Ben Hoyt
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of Ben Hoyt nor the names of its contributors
|
||||
may be used to endorse or promote products derived from this software
|
||||
without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY BEN HOYT ''AS IS'' AND ANY
|
||||
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL BEN HOYT BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
|
@ -1,22 +0,0 @@
|
|||
# Top level Automake for inih
|
||||
#
|
||||
# Makefile copyright (C) 2021 Nirenjan Krishnan (nirenjan@nirenjan.org)
|
||||
#
|
||||
# inih is copyright (C) 2009-2020, Ben Hoyt
|
||||
# inih is released under the New BSD license (see LICENSE.txt). Go to the
|
||||
# project home page for more info:
|
||||
# https://github.com/benhoyt/inih
|
||||
|
||||
ACLOCAL_AMFLAGS = -I m4
|
||||
|
||||
# Extra files that need to be in the distribution
|
||||
EXTRA_DIST = \
|
||||
LICENSE.txt \
|
||||
ini.h \
|
||||
README.md
|
||||
|
||||
noinst_LTLIBRARIES = libinih.la
|
||||
|
||||
libinih_la_SOURCES = ini.c
|
||||
libinih_la_CFLAGS = $(WARN_CFLAGS) -I $(top_builddir)
|
||||
libinih_la_LDFLAGS = $(WARN_LDFLAGS)
|
||||
|
|
@ -1,13 +0,0 @@
|
|||
# inih (INI Not Invented Here)
|
||||
|
||||
**inih (INI Not Invented Here)** is a simple [.INI
|
||||
file](http://en.wikipedia.org/wiki/INI_file) parser written in C. It's only a
|
||||
couple of pages of code, and it was designed to be _small and simple_, so it's
|
||||
good for embedded systems. It's also more or less compatible with Python's
|
||||
[ConfigParser](http://docs.python.org/library/configparser.html) style of .INI
|
||||
files, including RFC 822-style multi-line syntax and `name: value` entries.
|
||||
|
||||
The original project can be found [here](https://github.com/benhoyt/inih). I
|
||||
have included the sources in this repository because it is not packaged for all
|
||||
platforms (notably macOS), and the version here supercedes the version that is
|
||||
distributed in most "stable" Linux distributions such as Debian.
|
||||
298
lib/inih/ini.c
298
lib/inih/ini.c
|
|
@ -1,298 +0,0 @@
|
|||
/* inih -- simple .INI file parser
|
||||
|
||||
SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
Copyright (C) 2009-2020, Ben Hoyt
|
||||
|
||||
inih is released under the New BSD license (see LICENSE.txt). Go to the project
|
||||
home page for more info:
|
||||
|
||||
https://github.com/benhoyt/inih
|
||||
|
||||
*/
|
||||
|
||||
#if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS)
|
||||
#define _CRT_SECURE_NO_WARNINGS
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
#include <ctype.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "ini.h"
|
||||
|
||||
#if !INI_USE_STACK
|
||||
#if INI_CUSTOM_ALLOCATOR
|
||||
#include <stddef.h>
|
||||
void* ini_malloc(size_t size);
|
||||
void ini_free(void* ptr);
|
||||
void* ini_realloc(void* ptr, size_t size);
|
||||
#else
|
||||
#include <stdlib.h>
|
||||
#define ini_malloc malloc
|
||||
#define ini_free free
|
||||
#define ini_realloc realloc
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#define MAX_SECTION 50
|
||||
#define MAX_NAME 50
|
||||
|
||||
/* Used by ini_parse_string() to keep track of string parsing state. */
|
||||
typedef struct {
|
||||
const char* ptr;
|
||||
size_t num_left;
|
||||
} ini_parse_string_ctx;
|
||||
|
||||
/* Strip whitespace chars off end of given string, in place. Return s. */
|
||||
static char* rstrip(char* s)
|
||||
{
|
||||
char* p = s + strlen(s);
|
||||
while (p > s && isspace((unsigned char)(*--p)))
|
||||
*p = '\0';
|
||||
return s;
|
||||
}
|
||||
|
||||
/* Return pointer to first non-whitespace char in given string. */
|
||||
static char* lskip(const char* s)
|
||||
{
|
||||
while (*s && isspace((unsigned char)(*s)))
|
||||
s++;
|
||||
return (char*)s;
|
||||
}
|
||||
|
||||
/* Return pointer to first char (of chars) or inline comment in given string,
|
||||
or pointer to NUL at end of string if neither found. Inline comment must
|
||||
be prefixed by a whitespace character to register as a comment. */
|
||||
static char* find_chars_or_comment(const char* s, const char* chars)
|
||||
{
|
||||
#if INI_ALLOW_INLINE_COMMENTS
|
||||
int was_space = 0;
|
||||
while (*s && (!chars || !strchr(chars, *s)) &&
|
||||
!(was_space && strchr(INI_INLINE_COMMENT_PREFIXES, *s))) {
|
||||
was_space = isspace((unsigned char)(*s));
|
||||
s++;
|
||||
}
|
||||
#else
|
||||
while (*s && (!chars || !strchr(chars, *s))) {
|
||||
s++;
|
||||
}
|
||||
#endif
|
||||
return (char*)s;
|
||||
}
|
||||
|
||||
/* Similar to strncpy, but ensures dest (size bytes) is
|
||||
NUL-terminated, and doesn't pad with NULs. */
|
||||
static char* strncpy0(char* dest, const char* src, size_t size)
|
||||
{
|
||||
/* Could use strncpy internally, but it causes gcc warnings (see issue #91) */
|
||||
size_t i;
|
||||
for (i = 0; i < size - 1 && src[i]; i++)
|
||||
dest[i] = src[i];
|
||||
dest[i] = '\0';
|
||||
return dest;
|
||||
}
|
||||
|
||||
/* See documentation in header file. */
|
||||
int ini_parse_stream(ini_reader reader, void* stream, ini_handler handler,
|
||||
void* user)
|
||||
{
|
||||
/* Uses a fair bit of stack (use heap instead if you need to) */
|
||||
#if INI_USE_STACK
|
||||
char line[INI_MAX_LINE];
|
||||
int max_line = INI_MAX_LINE;
|
||||
#else
|
||||
char* line;
|
||||
size_t max_line = INI_INITIAL_ALLOC;
|
||||
#endif
|
||||
#if INI_ALLOW_REALLOC && !INI_USE_STACK
|
||||
char* new_line;
|
||||
size_t offset;
|
||||
#endif
|
||||
char section[MAX_SECTION] = "";
|
||||
char prev_name[MAX_NAME] = "";
|
||||
|
||||
char* start;
|
||||
char* end;
|
||||
char* name;
|
||||
char* value;
|
||||
int lineno = 0;
|
||||
int error = 0;
|
||||
|
||||
#if !INI_USE_STACK
|
||||
line = (char*)ini_malloc(INI_INITIAL_ALLOC);
|
||||
if (!line) {
|
||||
return -2;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if INI_HANDLER_LINENO
|
||||
#define HANDLER(u, s, n, v) handler(u, s, n, v, lineno)
|
||||
#else
|
||||
#define HANDLER(u, s, n, v) handler(u, s, n, v)
|
||||
#endif
|
||||
|
||||
/* Scan through stream line by line */
|
||||
while (reader(line, (int)max_line, stream) != NULL) {
|
||||
#if INI_ALLOW_REALLOC && !INI_USE_STACK
|
||||
offset = strlen(line);
|
||||
while (offset == max_line - 1 && line[offset - 1] != '\n') {
|
||||
max_line *= 2;
|
||||
if (max_line > INI_MAX_LINE)
|
||||
max_line = INI_MAX_LINE;
|
||||
new_line = ini_realloc(line, max_line);
|
||||
if (!new_line) {
|
||||
ini_free(line);
|
||||
return -2;
|
||||
}
|
||||
line = new_line;
|
||||
if (reader(line + offset, (int)(max_line - offset), stream) == NULL)
|
||||
break;
|
||||
if (max_line >= INI_MAX_LINE)
|
||||
break;
|
||||
offset += strlen(line + offset);
|
||||
}
|
||||
#endif
|
||||
|
||||
lineno++;
|
||||
|
||||
start = line;
|
||||
#if INI_ALLOW_BOM
|
||||
if (lineno == 1 && (unsigned char)start[0] == 0xEF &&
|
||||
(unsigned char)start[1] == 0xBB &&
|
||||
(unsigned char)start[2] == 0xBF) {
|
||||
start += 3;
|
||||
}
|
||||
#endif
|
||||
start = lskip(rstrip(start));
|
||||
|
||||
if (strchr(INI_START_COMMENT_PREFIXES, *start)) {
|
||||
/* Start-of-line comment */
|
||||
}
|
||||
#if INI_ALLOW_MULTILINE
|
||||
else if (*prev_name && *start && start > line) {
|
||||
/* Non-blank line with leading whitespace, treat as continuation
|
||||
of previous name's value (as per Python configparser). */
|
||||
if (!HANDLER(user, section, prev_name, start) && !error)
|
||||
error = lineno;
|
||||
}
|
||||
#endif
|
||||
else if (*start == '[') {
|
||||
/* A "[section]" line */
|
||||
end = find_chars_or_comment(start + 1, "]");
|
||||
if (*end == ']') {
|
||||
*end = '\0';
|
||||
strncpy0(section, start + 1, sizeof(section));
|
||||
*prev_name = '\0';
|
||||
#if INI_CALL_HANDLER_ON_NEW_SECTION
|
||||
if (!HANDLER(user, section, NULL, NULL) && !error)
|
||||
error = lineno;
|
||||
#endif
|
||||
}
|
||||
else if (!error) {
|
||||
/* No ']' found on section line */
|
||||
error = lineno;
|
||||
}
|
||||
}
|
||||
else if (*start) {
|
||||
/* Not a comment, must be a name[=:]value pair */
|
||||
end = find_chars_or_comment(start, "=:");
|
||||
if (*end == '=' || *end == ':') {
|
||||
*end = '\0';
|
||||
name = rstrip(start);
|
||||
value = end + 1;
|
||||
#if INI_ALLOW_INLINE_COMMENTS
|
||||
end = find_chars_or_comment(value, NULL);
|
||||
if (*end)
|
||||
*end = '\0';
|
||||
#endif
|
||||
value = lskip(value);
|
||||
rstrip(value);
|
||||
|
||||
/* Valid name[=:]value pair found, call handler */
|
||||
strncpy0(prev_name, name, sizeof(prev_name));
|
||||
if (!HANDLER(user, section, name, value) && !error)
|
||||
error = lineno;
|
||||
}
|
||||
else if (!error) {
|
||||
/* No '=' or ':' found on name[=:]value line */
|
||||
#if INI_ALLOW_NO_VALUE
|
||||
*end = '\0';
|
||||
name = rstrip(start);
|
||||
if (!HANDLER(user, section, name, NULL) && !error)
|
||||
error = lineno;
|
||||
#else
|
||||
error = lineno;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
#if INI_STOP_ON_FIRST_ERROR
|
||||
if (error)
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
|
||||
#if !INI_USE_STACK
|
||||
ini_free(line);
|
||||
#endif
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
/* See documentation in header file. */
|
||||
int ini_parse_file(FILE* file, ini_handler handler, void* user)
|
||||
{
|
||||
return ini_parse_stream((ini_reader)fgets, file, handler, user);
|
||||
}
|
||||
|
||||
/* See documentation in header file. */
|
||||
int ini_parse(const char* filename, ini_handler handler, void* user)
|
||||
{
|
||||
FILE* file;
|
||||
int error;
|
||||
|
||||
file = fopen(filename, "r");
|
||||
if (!file)
|
||||
return -1;
|
||||
error = ini_parse_file(file, handler, user);
|
||||
fclose(file);
|
||||
return error;
|
||||
}
|
||||
|
||||
/* An ini_reader function to read the next line from a string buffer. This
|
||||
is the fgets() equivalent used by ini_parse_string(). */
|
||||
static char* ini_reader_string(char* str, int num, void* stream) {
|
||||
ini_parse_string_ctx* ctx = (ini_parse_string_ctx*)stream;
|
||||
const char* ctx_ptr = ctx->ptr;
|
||||
size_t ctx_num_left = ctx->num_left;
|
||||
char* strp = str;
|
||||
char c;
|
||||
|
||||
if (ctx_num_left == 0 || num < 2)
|
||||
return NULL;
|
||||
|
||||
while (num > 1 && ctx_num_left != 0) {
|
||||
c = *ctx_ptr++;
|
||||
ctx_num_left--;
|
||||
*strp++ = c;
|
||||
if (c == '\n')
|
||||
break;
|
||||
num--;
|
||||
}
|
||||
|
||||
*strp = '\0';
|
||||
ctx->ptr = ctx_ptr;
|
||||
ctx->num_left = ctx_num_left;
|
||||
return str;
|
||||
}
|
||||
|
||||
/* See documentation in header file. */
|
||||
int ini_parse_string(const char* string, ini_handler handler, void* user) {
|
||||
ini_parse_string_ctx ctx;
|
||||
|
||||
ctx.ptr = string;
|
||||
ctx.num_left = strlen(string);
|
||||
return ini_parse_stream((ini_reader)ini_reader_string, &ctx, handler,
|
||||
user);
|
||||
}
|
||||
157
lib/inih/ini.h
157
lib/inih/ini.h
|
|
@ -1,157 +0,0 @@
|
|||
/* inih -- simple .INI file parser
|
||||
|
||||
SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
Copyright (C) 2009-2020, Ben Hoyt
|
||||
|
||||
inih is released under the New BSD license (see LICENSE.txt). Go to the project
|
||||
home page for more info:
|
||||
|
||||
https://github.com/benhoyt/inih
|
||||
|
||||
*/
|
||||
|
||||
#ifndef INI_H
|
||||
#define INI_H
|
||||
|
||||
/* Make this header file easier to include in C++ code */
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
/* Nonzero if ini_handler callback should accept lineno parameter. */
|
||||
#ifndef INI_HANDLER_LINENO
|
||||
#define INI_HANDLER_LINENO 0
|
||||
#endif
|
||||
|
||||
/* Typedef for prototype of handler function. */
|
||||
#if INI_HANDLER_LINENO
|
||||
typedef int (*ini_handler)(void* user, const char* section,
|
||||
const char* name, const char* value,
|
||||
int lineno);
|
||||
#else
|
||||
typedef int (*ini_handler)(void* user, const char* section,
|
||||
const char* name, const char* value);
|
||||
#endif
|
||||
|
||||
/* Typedef for prototype of fgets-style reader function. */
|
||||
typedef char* (*ini_reader)(char* str, int num, void* stream);
|
||||
|
||||
/* Parse given INI-style file. May have [section]s, name=value pairs
|
||||
(whitespace stripped), and comments starting with ';' (semicolon). Section
|
||||
is "" if name=value pair parsed before any section heading. name:value
|
||||
pairs are also supported as a concession to Python's configparser.
|
||||
|
||||
For each name=value pair parsed, call handler function with given user
|
||||
pointer as well as section, name, and value (data only valid for duration
|
||||
of handler call). Handler should return nonzero on success, zero on error.
|
||||
|
||||
Returns 0 on success, line number of first error on parse error (doesn't
|
||||
stop on first error), -1 on file open error, or -2 on memory allocation
|
||||
error (only when INI_USE_STACK is zero).
|
||||
*/
|
||||
int ini_parse(const char* filename, ini_handler handler, void* user);
|
||||
|
||||
/* Same as ini_parse(), but takes a FILE* instead of filename. This doesn't
|
||||
close the file when it's finished -- the caller must do that. */
|
||||
int ini_parse_file(FILE* file, ini_handler handler, void* user);
|
||||
|
||||
/* Same as ini_parse(), but takes an ini_reader function pointer instead of
|
||||
filename. Used for implementing custom or string-based I/O (see also
|
||||
ini_parse_string). */
|
||||
int ini_parse_stream(ini_reader reader, void* stream, ini_handler handler,
|
||||
void* user);
|
||||
|
||||
/* Same as ini_parse(), but takes a zero-terminated string with the INI data
|
||||
instead of a file. Useful for parsing INI data from a network socket or
|
||||
already in memory. */
|
||||
int ini_parse_string(const char* string, ini_handler handler, void* user);
|
||||
|
||||
/* Nonzero to allow multi-line value parsing, in the style of Python's
|
||||
configparser. If allowed, ini_parse() will call the handler with the same
|
||||
name for each subsequent line parsed. */
|
||||
#ifndef INI_ALLOW_MULTILINE
|
||||
#define INI_ALLOW_MULTILINE 1
|
||||
#endif
|
||||
|
||||
/* Nonzero to allow a UTF-8 BOM sequence (0xEF 0xBB 0xBF) at the start of
|
||||
the file. See https://github.com/benhoyt/inih/issues/21 */
|
||||
#ifndef INI_ALLOW_BOM
|
||||
#define INI_ALLOW_BOM 1
|
||||
#endif
|
||||
|
||||
/* Chars that begin a start-of-line comment. Per Python configparser, allow
|
||||
both ; and # comments at the start of a line by default. */
|
||||
#ifndef INI_START_COMMENT_PREFIXES
|
||||
#define INI_START_COMMENT_PREFIXES ";#"
|
||||
#endif
|
||||
|
||||
/* Nonzero to allow inline comments (with valid inline comment characters
|
||||
specified by INI_INLINE_COMMENT_PREFIXES). Set to 0 to turn off and match
|
||||
Python 3.2+ configparser behaviour. */
|
||||
#ifndef INI_ALLOW_INLINE_COMMENTS
|
||||
#define INI_ALLOW_INLINE_COMMENTS 1
|
||||
#endif
|
||||
#ifndef INI_INLINE_COMMENT_PREFIXES
|
||||
#define INI_INLINE_COMMENT_PREFIXES ";"
|
||||
#endif
|
||||
|
||||
/* Nonzero to use stack for line buffer, zero to use heap (malloc/free). */
|
||||
#ifndef INI_USE_STACK
|
||||
#define INI_USE_STACK 1
|
||||
#endif
|
||||
|
||||
/* Maximum line length for any line in INI file (stack or heap). Note that
|
||||
this must be 3 more than the longest line (due to '\r', '\n', and '\0'). */
|
||||
#ifndef INI_MAX_LINE
|
||||
#define INI_MAX_LINE 200
|
||||
#endif
|
||||
|
||||
/* Nonzero to allow heap line buffer to grow via realloc(), zero for a
|
||||
fixed-size buffer of INI_MAX_LINE bytes. Only applies if INI_USE_STACK is
|
||||
zero. */
|
||||
#ifndef INI_ALLOW_REALLOC
|
||||
#define INI_ALLOW_REALLOC 0
|
||||
#endif
|
||||
|
||||
/* Initial size in bytes for heap line buffer. Only applies if INI_USE_STACK
|
||||
is zero. */
|
||||
#ifndef INI_INITIAL_ALLOC
|
||||
#define INI_INITIAL_ALLOC 200
|
||||
#endif
|
||||
|
||||
/* Stop parsing on first error (default is to keep parsing). */
|
||||
#ifndef INI_STOP_ON_FIRST_ERROR
|
||||
#define INI_STOP_ON_FIRST_ERROR 0
|
||||
#endif
|
||||
|
||||
/* Nonzero to call the handler at the start of each new section (with
|
||||
name and value NULL). Default is to only call the handler on
|
||||
each name=value pair. */
|
||||
#ifndef INI_CALL_HANDLER_ON_NEW_SECTION
|
||||
#define INI_CALL_HANDLER_ON_NEW_SECTION 0
|
||||
#endif
|
||||
|
||||
/* Nonzero to allow a name without a value (no '=' or ':' on the line) and
|
||||
call the handler with value NULL in this case. Default is to treat
|
||||
no-value lines as an error. */
|
||||
#ifndef INI_ALLOW_NO_VALUE
|
||||
#define INI_ALLOW_NO_VALUE 0
|
||||
#endif
|
||||
|
||||
/* Nonzero to use custom ini_malloc, ini_free, and ini_realloc memory
|
||||
allocation functions (INI_USE_STACK must also be 0). These functions must
|
||||
have the same signatures as malloc/free/realloc and behave in a similar
|
||||
way. ini_realloc is only needed if INI_ALLOW_REALLOC is set. */
|
||||
#ifndef INI_CUSTOM_ALLOCATOR
|
||||
#define INI_CUSTOM_ALLOCATOR 0
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* INI_H */
|
||||
|
|
@ -31,7 +31,9 @@ static libusb_device_handle *libusbx52_init(void)
|
|||
int rc;
|
||||
ssize_t count;
|
||||
int i;
|
||||
libusb_device **list;
|
||||
libusb_device dummy = { 0 };
|
||||
libusb_device *dummy_list[] = { &dummy };
|
||||
libusb_device **list = dummy_list;
|
||||
libusb_device_handle *hdl = NULL;
|
||||
struct libusb_device_descriptor desc;
|
||||
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ lib_LTLIBRARIES += libx52.la
|
|||
# See: https://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html
|
||||
libx52_v_CUR=6
|
||||
libx52_v_AGE=4
|
||||
libx52_v_REV=1
|
||||
libx52_v_REV=2
|
||||
libx52_la_SOURCES = \
|
||||
libx52/x52_control.c \
|
||||
libx52/x52_core.c \
|
||||
|
|
@ -43,7 +43,7 @@ check_PROGRAMS += libx52test libx52-string-test
|
|||
|
||||
nodist_libx52test_SOURCES = libx52/test_libx52.c
|
||||
libx52test_SOURCES = $(libx52_la_SOURCES)
|
||||
libx52test_CFLAGS = @LIBUSB_CFLAGS@ -DLOCALEDIR='"$(localedir)"' -I $(top_srcdir) -I $(top_srcdir)/libx52
|
||||
libx52test_CFLAGS = @CMOCKA_CFLAGS@ @LIBUSB_CFLAGS@ -DLOCALEDIR='"$(localedir)"' -I $(top_srcdir) -I $(top_srcdir)/libx52
|
||||
libx52test_CFLAGS += -Dlibusb_control_transfer=__wrap_libusb_control_transfer
|
||||
libx52test_LDFLAGS = @CMOCKA_LIBS@ @LIBUSB_LIBS@
|
||||
libx52test_LDADD = libx52.la
|
||||
|
|
@ -60,7 +60,7 @@ libx52_string_test_SOURCES = \
|
|||
libx52/test_strings.c \
|
||||
libx52/x52_stringify.c \
|
||||
libx52/x52_strerror.c
|
||||
libx52_string_test_CFLAGS = -I $(top_srcdir) -I $(top_srcdir)/libx52
|
||||
libx52_string_test_CFLAGS = @CMOCKA_CFLAGS@ -I $(top_srcdir) -I $(top_srcdir)/libx52
|
||||
libx52_string_test_LDFLAGS = @CMOCKA_LIBS@
|
||||
libx52_string_test_LDADD = libx52.la
|
||||
endif
|
||||
|
|
|
|||
|
|
@ -285,7 +285,6 @@ int libx52_init(libx52_device ** dev);
|
|||
* cause errors.
|
||||
*
|
||||
* @param[in] dev Pointer to the device context
|
||||
* @returns None
|
||||
*/
|
||||
void libx52_exit(libx52_device *dev);
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,57 @@
|
|||
libx52_version = '2.4.2'
|
||||
|
||||
libx52_files = files(
|
||||
'x52_control.c',
|
||||
'x52_core.c',
|
||||
'x52_date_time.c',
|
||||
'x52_mfd_led.c',
|
||||
'x52_strerror.c',
|
||||
'x52_stringify.c',
|
||||
)
|
||||
|
||||
lib_libx52 = library('x52', libx52_files,
|
||||
install: true,
|
||||
version: libx52_version,
|
||||
dependencies: [dep_libusb, dep_intl],
|
||||
include_directories: [includes])
|
||||
|
||||
install_headers('libx52.h', subdir: meson.project_name())
|
||||
pkgconfig.generate(lib_libx52,
|
||||
name: 'libx52',
|
||||
description: 'Linux/Unix library to control Saitek X52/X52Pro joystick extended functionality.',
|
||||
subdirs: meson.project_name(),
|
||||
version: libx52_version)
|
||||
|
||||
# Unit tests for libx52
|
||||
libx52_string_test = executable('libx52-string-test',
|
||||
'test_strings.c',
|
||||
'x52_stringify.c',
|
||||
'x52_strerror.c',
|
||||
build_by_default: false,
|
||||
dependencies: [dep_cmocka, dep_intl],
|
||||
link_with: [lib_libx52],
|
||||
include_directories: [includes, lib_libx52.private_dir_include()],
|
||||
)
|
||||
|
||||
test('libx52-string-test', libx52_string_test, protocol: 'tap')
|
||||
|
||||
test_gen_script = files('x52_test_gen.py')[0]
|
||||
|
||||
libx52_test_file = custom_target('libx52-test',
|
||||
build_by_default: false,
|
||||
depend_files: ['x52_test_gen.py', 'x52_tests.json'],
|
||||
command: [python, test_gen_script, '@INPUT@'],
|
||||
capture: true,
|
||||
input: 'x52_tests.json',
|
||||
output: 'test_libx52.c'
|
||||
)
|
||||
|
||||
libx52test = executable('libx52test', libx52_test_file, libx52_files,
|
||||
c_args: ['-Dlibusb_control_transfer=__wrap_libusb_control_transfer'],
|
||||
dependencies: [dep_cmocka, dep_libusb, dep_intl],
|
||||
link_with: [lib_libx52],
|
||||
build_by_default: false,
|
||||
include_directories: [includes, lib_libx52.private_dir_include()],
|
||||
)
|
||||
|
||||
test('libx52test', libx52test, protocol: 'tap')
|
||||
|
|
@ -17,8 +17,10 @@
|
|||
|
||||
#define TEST_STRINGIFY(name) do { \
|
||||
char expected[256]; \
|
||||
for (int i=-1; i < sizeof(name ## _map) / sizeof(name ## _map[0]); i++) { \
|
||||
if (name ## _map[i] != NULL) { \
|
||||
for (int i=-1; i < (int)(sizeof(name ## _map) / sizeof(name ## _map[0])); i++) { \
|
||||
if (i < 0) { \
|
||||
snprintf(expected, sizeof(expected), unknown_fmt, i); \
|
||||
} else if (name ## _map[i] != NULL) { \
|
||||
strncpy(expected, name ## _map[i], sizeof(expected)); \
|
||||
} else { \
|
||||
snprintf(expected, sizeof(expected), unknown_fmt, i); \
|
||||
|
|
@ -29,6 +31,8 @@
|
|||
|
||||
static void test_led_id_names(void **state)
|
||||
{
|
||||
(void)state; // Suppress unused parameter warning
|
||||
|
||||
static const char * led_id_map[21] = {
|
||||
[LIBX52_LED_FIRE] = "Fire",
|
||||
[LIBX52_LED_A] = "A",
|
||||
|
|
@ -50,6 +54,8 @@ static void test_led_id_names(void **state)
|
|||
|
||||
static void test_led_state_names(void **state)
|
||||
{
|
||||
(void)state; // Suppress unused parameter warning
|
||||
|
||||
static const char * led_state_map[6] = {
|
||||
[LIBX52_LED_STATE_OFF] = "off",
|
||||
[LIBX52_LED_STATE_ON] = "on",
|
||||
|
|
@ -64,6 +70,8 @@ static void test_led_state_names(void **state)
|
|||
}
|
||||
|
||||
static void test_clock_id_names(void **state) {
|
||||
(void)state; // Suppress unused parameter warning
|
||||
|
||||
static const char * clock_id_map[4] = {
|
||||
[LIBX52_CLOCK_1] = "primary",
|
||||
[LIBX52_CLOCK_2] = "secondary",
|
||||
|
|
@ -76,6 +84,8 @@ static void test_clock_id_names(void **state) {
|
|||
}
|
||||
|
||||
static void test_clock_format_names(void **state) {
|
||||
(void)state; // Suppress unused parameter warning
|
||||
|
||||
static const char * clock_format_map[3] = {
|
||||
[LIBX52_CLOCK_FORMAT_12HR] = "12 hour",
|
||||
[LIBX52_CLOCK_FORMAT_24HR] = "24 hour",
|
||||
|
|
@ -87,6 +97,8 @@ static void test_clock_format_names(void **state) {
|
|||
}
|
||||
|
||||
static void test_date_format_names(void **state) {
|
||||
(void)state; // Suppress unused parameter warning
|
||||
|
||||
static const char * date_format_map[4] = {
|
||||
[LIBX52_DATE_FORMAT_DDMMYY] = "DD-MM-YY",
|
||||
[LIBX52_DATE_FORMAT_MMDDYY] = "MM-DD-YY",
|
||||
|
|
@ -101,6 +113,8 @@ static void test_date_format_names(void **state) {
|
|||
#define libx52_error_to_str libx52_strerror
|
||||
|
||||
static void test_strerror(void **state) {
|
||||
(void)state; // Suppress unused parameter warning
|
||||
|
||||
static const char *error_map[18] = {
|
||||
[LIBX52_SUCCESS] = "Success",
|
||||
[LIBX52_ERROR_INIT_FAILURE] = "Initialization failure",
|
||||
|
|
|
|||
|
|
@ -103,6 +103,8 @@ int libx52_vendor_command(libx52_device *x52, uint16_t index, uint16_t value)
|
|||
static int _x52_write_shift(libx52_device *x52, uint32_t bit)
|
||||
{
|
||||
uint16_t value;
|
||||
(void)bit;
|
||||
|
||||
value = tst_bit(&x52->led_mask, X52_BIT_SHIFT) ? X52_SHIFT_ON : X52_SHIFT_OFF;
|
||||
return libx52_vendor_command(x52, X52_SHIFT_INDICATOR, value);
|
||||
}
|
||||
|
|
@ -152,6 +154,7 @@ static int _x52_write_line(libx52_device *x52, uint32_t bit)
|
|||
static int _x52_write_pov_blink(libx52_device *x52, uint32_t bit)
|
||||
{
|
||||
uint16_t value;
|
||||
(void)bit;
|
||||
value = tst_bit(&x52->led_mask, X52_BIT_POV_BLINK) ? X52_BLINK_ON : X52_BLINK_OFF;
|
||||
return libx52_vendor_command(x52, X52_BLINK_INDICATOR, value);
|
||||
}
|
||||
|
|
@ -177,6 +180,7 @@ static int _x52_write_date(libx52_device *x52, uint32_t bit)
|
|||
uint16_t value1; //dd-mm
|
||||
uint16_t value2; //yy
|
||||
int rc;
|
||||
(void)bit;
|
||||
|
||||
switch (x52->date_format) {
|
||||
case LIBX52_DATE_FORMAT_YYMMDD:
|
||||
|
|
@ -288,7 +292,7 @@ static int _x52_write_time(libx52_device *x52, uint32_t bit)
|
|||
|
||||
typedef int (*x52_handler)(libx52_device *, uint32_t);
|
||||
|
||||
const x52_handler _x52_handlers[32] = {
|
||||
static const x52_handler _x52_handlers[32] = {
|
||||
[X52_BIT_SHIFT] = _x52_write_shift,
|
||||
[X52_BIT_LED_FIRE] = _x52_write_led,
|
||||
[X52_BIT_LED_A_RED] = _x52_write_led,
|
||||
|
|
|
|||
|
|
@ -48,6 +48,8 @@ static int _x52_hotplug_callback(libusb_context *ctx,
|
|||
{
|
||||
libx52_device *dev = user_data;
|
||||
|
||||
(void)device; // Suppress unused parameter warning
|
||||
|
||||
if (dev == NULL) {
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -229,6 +231,10 @@ int libx52_init(libx52_device **dev)
|
|||
|
||||
void libx52_exit(libx52_device *dev)
|
||||
{
|
||||
if (!dev) {
|
||||
return;
|
||||
}
|
||||
|
||||
libx52_disconnect(dev);
|
||||
libusb_exit(dev->ctx);
|
||||
|
||||
|
|
|
|||
|
|
@ -73,6 +73,7 @@ int __wrap_libusb_control_transfer(libusb_device_handle *dev_handle,
|
|||
uint16_t wLength,
|
||||
unsigned int timeout)
|
||||
{
|
||||
(void)dev_handle;
|
||||
function_called();
|
||||
check_expected(wIndex);
|
||||
check_expected(wValue);
|
||||
|
|
|
|||
|
|
@ -37,12 +37,12 @@ TESTS += test-axis test-parser
|
|||
check_PROGRAMS += test-axis test-parser
|
||||
|
||||
test_axis_SOURCES = libx52io/test_axis.c $(libx52io_la_SOURCES)
|
||||
test_axis_CFLAGS = $(libx52io_la_CFLAGS)
|
||||
test_axis_CFLAGS = @CMOCKA_CFLAGS@ $(libx52io_la_CFLAGS)
|
||||
test_axis_LDFLAGS = @CMOCKA_LIBS@ @HIDAPI_LIBS@ $(WARN_LDFLAGS)
|
||||
test_axis_LDADD = @LTLIBINTL@
|
||||
|
||||
test_parser_SOURCES = libx52io/test_parser.c $(libx52io_la_SOURCES)
|
||||
test_parser_CFLAGS = $(libx52io_la_CFLAGS)
|
||||
test_parser_CFLAGS = @CMOCKA_CFLAGS@ $(libx52io_la_CFLAGS)
|
||||
test_parser_LDFLAGS = @CMOCKA_LIBS@ @HIDAPI_LIBS@ $(WARN_LDFLAGS)
|
||||
test_parser_LDADD = @LTLIBINTL@
|
||||
|
||||
|
|
|
|||
|
|
@ -303,7 +303,6 @@ int libx52io_init(libx52io_context **ctx);
|
|||
* cause errors.
|
||||
*
|
||||
* @param[in] ctx Pointer to the device context
|
||||
* @returns None
|
||||
*/
|
||||
void libx52io_exit(libx52io_context *ctx);
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,39 @@
|
|||
libx52io_version = '1.0.0'
|
||||
|
||||
libx52io_files = files(
|
||||
'io_core.c',
|
||||
'io_axis.c',
|
||||
'io_parser.c',
|
||||
'io_strings.c',
|
||||
'io_device.c',
|
||||
)
|
||||
|
||||
lib_libx52io = library('x52io', libx52io_files,
|
||||
install: true,
|
||||
version: libx52io_version,
|
||||
dependencies: [dep_hidapi, dep_intl],
|
||||
include_directories: [includes])
|
||||
|
||||
install_headers('libx52io.h', subdir: meson.project_name())
|
||||
pkgconfig.generate(lib_libx52io,
|
||||
name: 'libx52io',
|
||||
description: 'Linux/Unix library to read and parse X52 input',
|
||||
subdirs: meson.project_name(),
|
||||
version: libx52io_version,
|
||||
)
|
||||
|
||||
test_axis = executable('test-axis', 'test_axis.c', libx52io_files,
|
||||
build_by_default: false,
|
||||
dependencies: [dep_cmocka, dep_hidapi, dep_intl],
|
||||
include_directories: [includes],
|
||||
)
|
||||
test('test-axis', test_axis, protocol: 'tap')
|
||||
|
||||
test_parser = executable('test-parser', 'test_parser.c', libx52io_files,
|
||||
build_by_default: false,
|
||||
dependencies: [dep_cmocka, dep_hidapi, dep_intl],
|
||||
include_directories: [includes],
|
||||
)
|
||||
test('test-parser', test_parser, protocol: 'tap')
|
||||
|
||||
|
||||
|
|
@ -89,7 +89,8 @@ static int group_teardown(void **state)
|
|||
{ \
|
||||
libx52io_context *ctx = *state; \
|
||||
int rc; \
|
||||
int32_t min, max; \
|
||||
int32_t min = 0; \
|
||||
int32_t max = 0; \
|
||||
ctx->pid = X52_PROD_X52 ## prodid; \
|
||||
_x52io_set_axis_range(ctx); \
|
||||
rc = libx52io_get_axis_range(ctx, LIBX52IO_AXIS_ ## axis, &min, &max); \
|
||||
|
|
|
|||
|
|
@ -38,9 +38,9 @@ static int group_setup(void **state)
|
|||
return 0; \
|
||||
}
|
||||
|
||||
TEST_SETUP_FUNCTION(_1);
|
||||
TEST_SETUP_FUNCTION(_2);
|
||||
TEST_SETUP_FUNCTION(PRO);
|
||||
TEST_SETUP_FUNCTION(_1)
|
||||
TEST_SETUP_FUNCTION(_2)
|
||||
TEST_SETUP_FUNCTION(PRO)
|
||||
|
||||
#undef TEST_SETUP_FUNCTION
|
||||
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ lib_LTLIBRARIES += libx52util.la
|
|||
nodist_libx52util_la_SOURCES = libx52util/util_char_map.c
|
||||
libx52util_la_SOURCES = libx52util/x52_char_map_lookup.c
|
||||
libx52util_la_CFLAGS = -I $(top_srcdir)/libx52util $(WARN_CFLAGS)
|
||||
libx52util_la_LDFLAGS = -version-info 1:0:0 $(WARN_LDFLAGS)
|
||||
libx52util_la_LDFLAGS = -version-info 1:1:0 $(WARN_LDFLAGS)
|
||||
|
||||
# Header files that need to be copied
|
||||
x52include_HEADERS += libx52util/libx52util.h
|
||||
|
|
@ -20,13 +20,25 @@ x52include_HEADERS += libx52util/libx52util.h
|
|||
pkgconfig_DATA += libx52util/libx52util.pc
|
||||
|
||||
# Autogenerated file that needs to be cleaned up
|
||||
CLEANFILES += libx52util/util_char_map.c
|
||||
util_char_map_c_DEPENDS = \
|
||||
char_map_TARGETS = \
|
||||
libx52util/util_char_map.c \
|
||||
libx52util/x52_char_map.bin
|
||||
|
||||
CLEANFILES += $(char_map_TARGETS)
|
||||
|
||||
char_map_DEPENDS = \
|
||||
$(srcdir)/libx52util/x52_char_map_gen.py \
|
||||
$(srcdir)/libx52util/x52_char_map.cfg
|
||||
|
||||
libx52util/util_char_map.c: $(util_char_map_c_DEPENDS)
|
||||
$(AM_V_GEN) $(PYTHON) $(util_char_map_c_DEPENDS) $@
|
||||
$(char_map_TARGETS): $(char_map_DEPENDS)
|
||||
$(AM_V_GEN) $(PYTHON) $(char_map_DEPENDS) $(char_map_TARGETS)
|
||||
|
||||
TESTS += libx52util/test-runner.sh
|
||||
|
||||
check_PROGRAMS += libx52util-bmp-test
|
||||
libx52util_bmp_test_SOURCES = libx52util/x52_char_map_test.c
|
||||
libx52util_bmp_test_CFLAGS = -I $(top_srcdir)/libx52util
|
||||
libx52util_bmp_test_LDADD = libx52util.la
|
||||
|
||||
# Extra files that need to be in the distribution
|
||||
EXTRA_DIST += libx52util/x52_char_map.cfg \
|
||||
|
|
|
|||
|
|
@ -0,0 +1,40 @@
|
|||
# libx52util
|
||||
libx52util_version = '1.0.2'
|
||||
gen_script = files('x52_char_map_gen.py')[0]
|
||||
|
||||
util_char_map = custom_target('util-char-map',
|
||||
build_by_default: false,
|
||||
depend_files: ['x52_char_map_gen.py', 'x52_char_map.cfg'],
|
||||
command: [python, gen_script, '@INPUT@', '@OUTPUT0@', '@OUTPUT1@'],
|
||||
input: 'x52_char_map.cfg',
|
||||
output: ['util_char_map.c', 'x52_char_map.bin'])
|
||||
|
||||
lib_libx52util = library('x52util', util_char_map, 'x52_char_map_lookup.c',
|
||||
install: true,
|
||||
version: libx52util_version,
|
||||
include_directories: [includes],
|
||||
)
|
||||
|
||||
install_headers('libx52util.h', subdir: meson.project_name())
|
||||
pkgconfig.generate(lib_libx52util,
|
||||
name: 'libx52util',
|
||||
description: 'Extra utility functions to manage X52 extended functionality',
|
||||
subdirs: meson.project_name(),
|
||||
version: libx52util_version,
|
||||
)
|
||||
|
||||
libx52util_bmp_test = executable(
|
||||
'libx52util-bmp-test',
|
||||
'x52_char_map_test.c',
|
||||
build_by_default: false,
|
||||
include_directories: [includes, lib_libx52util.private_dir_include()],
|
||||
link_with: [lib_libx52util]
|
||||
)
|
||||
|
||||
test('libx52util-bmp-test', libx52util_bmp_test,
|
||||
protocol: 'tap',
|
||||
args: [util_char_map[1]])
|
||||
|
||||
benchmark('libx52util-bmp-bench', libx52util_bmp_test,
|
||||
protocol: 'tap',
|
||||
args: [util_char_map[1]])
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
#!/bin/sh
|
||||
|
||||
TEST_RUNNER="./libx52util-bmp-test"
|
||||
TEST_BIN="./libx52util/x52_char_map.bin"
|
||||
|
||||
if [ -e "${TEST_RUNNER}" ] && [ -e "${TEST_BIN}" ];
|
||||
then
|
||||
"${TEST_RUNNER}" "${TEST_BIN}"
|
||||
else
|
||||
echo "TAP version 13"
|
||||
echo "1..0 # missing files"
|
||||
fi
|
||||
|
|
@ -324,3 +324,13 @@
|
|||
0xFF9E 0xDE # HALFWIDTH KATAKANA VOICED SOUND MARK
|
||||
0xFF9F 0xDF # HALFWIDTH KATAKANA SEMI-VOICED SOUND MARK
|
||||
|
||||
# The following characters are manually added to aid in normalization to the
|
||||
# X52 character map
|
||||
0x2215 0x2F # DIVISION SLASH
|
||||
0x2044 0x2F # FRACTION SLASH
|
||||
0x00B0 0xDF # DEGREE SIGN
|
||||
# Note: while Greek letters aren't actually supported by the MFD character map,
|
||||
# this is manually addded to map the letter 'mu' to ASCII 'u'. This is needed
|
||||
# in the CJK compatibility page (0x3300-0x33FF) to deal with the square latin
|
||||
# abbreviations
|
||||
0x03BC 0x75 # GREEK SMALL LETTER MU
|
||||
|
|
|
|||
|
|
@ -12,20 +12,7 @@
|
|||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
enum {
|
||||
TYPE_INVALID = 0, /* Invalid type (default) */
|
||||
|
||||
TYPE_POINTER, /* Pointer target */
|
||||
|
||||
TYPE_ENTRY /* Map entry value */
|
||||
};
|
||||
|
||||
struct map_entry {
|
||||
struct map_entry *next; /* Pointer to the next table */
|
||||
uint8_t type; /* Type of entry */
|
||||
uint8_t value; /* Value is valid if this is of TYPE_ENTRY */
|
||||
};
|
||||
|
||||
extern struct map_entry map_root[];
|
||||
extern const uint16_t *root_table[256];
|
||||
extern const uint8_t *sequence_table[];
|
||||
|
||||
#endif /* !defined X52_CHAR_MAP_H */
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
#!/usr/bin/env python
|
||||
#!/usr/bin/env python3
|
||||
# Character map generator
|
||||
#
|
||||
# Copyright (C) 2012-2018 Nirenjan Krishnan (nirenjan@nirenjan.org)
|
||||
# Copyright (C) 2012-2026 Nirenjan Krishnan (nirenjan@nirenjan.org)
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-2.0-only WITH Classpath-exception-2.0
|
||||
"""
|
||||
|
|
@ -11,191 +11,267 @@ for the X52/X52 Pro MFD
|
|||
|
||||
import sys
|
||||
import re
|
||||
|
||||
AUTOGEN_HEADER = """
|
||||
/*
|
||||
* Autogenerated character map file for Saitek X52 Pro
|
||||
* Generated from %s
|
||||
*/
|
||||
|
||||
#include "x52_char_map.h"
|
||||
|
||||
"""
|
||||
|
||||
|
||||
class MapTable(object):
|
||||
"""
|
||||
Defines a MapTable entry, with each entry storing the value seen so far,
|
||||
the type of the entry, and the value, if it's a value node.
|
||||
"""
|
||||
# Empty list
|
||||
root = [None] * 256
|
||||
|
||||
def __init__(self, value_so_far, map_value=None):
|
||||
self.next_level = [None] * 256
|
||||
self.value_so_far = value_so_far
|
||||
self.map_value = map_value
|
||||
|
||||
def output_nodes(self):
|
||||
"""
|
||||
Output the individual nodes
|
||||
"""
|
||||
output_lines = []
|
||||
output_count = 0
|
||||
for node in self.next_level:
|
||||
if node is not None:
|
||||
output_lines.extend(node.output_nodes())
|
||||
output_count += 1
|
||||
|
||||
if output_count != 0:
|
||||
struct_header = 'static struct map_entry table_%x[64] = {' % \
|
||||
self.value_so_far
|
||||
output_lines.append(struct_header)
|
||||
|
||||
for node_index in range(0, 256):
|
||||
node = self.next_level[node_index]
|
||||
if node is not None:
|
||||
output_lines.append(self.dump_entry_line(0x80, node_index,
|
||||
node.value_so_far,
|
||||
node.map_value))
|
||||
|
||||
output_lines.extend(['};', ''])
|
||||
|
||||
return output_lines
|
||||
|
||||
@staticmethod
|
||||
def dump_entry_line(offset, node_index, value_so_far, map_value):
|
||||
"""
|
||||
Dump the array entry for the current node
|
||||
"""
|
||||
if map_value is None:
|
||||
node_entry_line = '\t[0x%02x] = { table_%x, TYPE_POINTER, 0 },' % \
|
||||
(node_index - offset, value_so_far)
|
||||
else:
|
||||
node_entry_line = '\t[0x%02x] = { NULL, TYPE_ENTRY, 0x%02x },' % \
|
||||
(node_index - offset, map_value)
|
||||
|
||||
return node_entry_line
|
||||
|
||||
@classmethod
|
||||
def add_to_table(cls, input_val, map_val):
|
||||
"""
|
||||
Add a map value to the lookup table
|
||||
"""
|
||||
try:
|
||||
uchr = unichr(input_val)
|
||||
except NameError:
|
||||
# Python 3 doesn't have unichr, but chr should work
|
||||
uchr = chr(input_val)
|
||||
|
||||
utf8_str = uchr.encode('utf-8')
|
||||
# Python2 returns the encoded result as a string, wheras
|
||||
# Python3 returns the result as a bytearray. Converting
|
||||
# the string (or bytearray) into a bytearray ensures that
|
||||
# this can be run in both Python2 and Python3
|
||||
utf8_vals = [c for c in bytearray(utf8_str)]
|
||||
|
||||
value_so_far = 0
|
||||
level = cls.root
|
||||
for index, char in enumerate(utf8_vals):
|
||||
value_so_far = (value_so_far << 8) | char
|
||||
if index < (len(utf8_vals) - 1):
|
||||
node = level[char]
|
||||
if node is None:
|
||||
node = cls(value_so_far)
|
||||
level[char] = node
|
||||
|
||||
level = level[char].next_level
|
||||
else:
|
||||
node = cls(value_so_far, map_val)
|
||||
level[char] = node
|
||||
|
||||
@classmethod
|
||||
def output_table_as_list(cls):
|
||||
"""
|
||||
Output the map table as a list of lines
|
||||
"""
|
||||
output_lines = []
|
||||
for node in cls.root:
|
||||
if node is not None:
|
||||
output_lines.extend(node.output_nodes())
|
||||
|
||||
output_lines.append('struct map_entry map_root[256] = {')
|
||||
|
||||
for node_index in range(0, 256):
|
||||
node = cls.root[node_index]
|
||||
if node is not None:
|
||||
output_lines.append(cls.dump_entry_line(0x0, node_index,
|
||||
node.value_so_far,
|
||||
node.map_value))
|
||||
|
||||
output_lines.extend(['};', ''])
|
||||
|
||||
return output_lines
|
||||
|
||||
import json
|
||||
import unicodedata
|
||||
|
||||
class LineFormatError(ValueError):
|
||||
"""
|
||||
Error class for parser
|
||||
"""
|
||||
|
||||
def parse_line(data):
|
||||
class BMPTable:
|
||||
"""
|
||||
Parse a line containing a mapping descriptor. The mapping descriptor
|
||||
must start with a hexadecimal unicode code point, followed by either a
|
||||
single character, or a hexadecimal integer that corresponds to the map
|
||||
value.
|
||||
Sparse table for Basic Multilingual Plane
|
||||
"""
|
||||
# Strip off comments
|
||||
data = re.sub(re.compile('#.*$'), '', data)
|
||||
|
||||
# Strip off leading and trailing whitespace
|
||||
data = data.strip()
|
||||
REPLACEMENT_CHAR = 0xDB
|
||||
|
||||
# If the line is empty, it is a comment line
|
||||
if len(data) == 0:
|
||||
return None, None
|
||||
HEADER = f"""/*
|
||||
* Autogenerated tables for X52 MFD character lookup
|
||||
*
|
||||
* DO NOT EDIT
|
||||
*/
|
||||
|
||||
# Find the code point and the target value
|
||||
try:
|
||||
code_point, target = data.strip().split()
|
||||
except ValueError:
|
||||
# Raised when there are either too many, or not enough values in
|
||||
# the string
|
||||
raise LineFormatError('Invalid descriptor format "%s"' % data)
|
||||
#include <stdint.h>
|
||||
|
||||
# Convert the string to its equivalent numeric value
|
||||
try:
|
||||
code_point = int(code_point, 0)
|
||||
except ValueError:
|
||||
raise LineFormatError('Invalid code point "%s"' % code_point)
|
||||
"""
|
||||
|
||||
# Check if the target is a single character
|
||||
if len(target) == 1:
|
||||
target = ord(target)
|
||||
else:
|
||||
# Try parsing the target as an integer
|
||||
TABLE_NAME_FORMAT = 'bmp_page_%02x'
|
||||
TABLE_NAME_DEFAULT = 'bmp_page_default'
|
||||
TABLE_FORMAT = 'const uint16_t %s[256] = {'
|
||||
TABLE_FOOTER = '};\n'
|
||||
|
||||
def __init__(self, input_file, output_file, output_map):
|
||||
self.input_file = input_file
|
||||
self.output_file = output_file
|
||||
self.output_map = output_map
|
||||
self.mapping = {}
|
||||
self.pages = {}
|
||||
self.sequences = {}
|
||||
self.root_table = []
|
||||
|
||||
self.read_map()
|
||||
self.build_extended_map()
|
||||
self.build_tables()
|
||||
self.generate_test_tables()
|
||||
|
||||
@staticmethod
|
||||
def parse_line(data):
|
||||
"""
|
||||
Parse a line containing a mapping descriptor. The mapping descriptor
|
||||
must start with a hexadecimal unicode code point, followed by either a
|
||||
single character, or a hexadecimal integer that corresponds to the map
|
||||
value.
|
||||
"""
|
||||
# Strip off comments
|
||||
data = re.sub(re.compile('#.*$'), '', data)
|
||||
|
||||
# Strip off leading and trailing whitespace
|
||||
data = data.strip()
|
||||
|
||||
# If the line is empty, it is a comment line
|
||||
if len(data) == 0:
|
||||
return None, None
|
||||
|
||||
# Find the code point and the target value
|
||||
try:
|
||||
target = int(target, 0)
|
||||
except ValueError:
|
||||
raise LineFormatError('Invalid map value "%s"' % target)
|
||||
code_point, target = data.strip().split()
|
||||
except ValueError as exc:
|
||||
# Raised when there are either too many, or not enough values in
|
||||
# the string
|
||||
raise LineFormatError(f'Invalid descriptor format "{data}"') from exc
|
||||
|
||||
return code_point, target
|
||||
# Convert the string to its equivalent numeric value
|
||||
try:
|
||||
code_point = int(code_point, 0)
|
||||
except ValueError as exc:
|
||||
raise LineFormatError(f'Invalid code point "{code_point}"') from exc
|
||||
|
||||
# Check if the target is a single character
|
||||
if len(target) == 1:
|
||||
target = ord(target)
|
||||
else:
|
||||
# Try parsing the target as an integer
|
||||
try:
|
||||
target = int(target, 0)
|
||||
except ValueError as exc:
|
||||
raise LineFormatError(f'Invalid map value "{target}"') from exc
|
||||
|
||||
return code_point, target
|
||||
|
||||
def read_map(self):
|
||||
"""Read the mapping tables from the config file"""
|
||||
def map_normalized(char, dst):
|
||||
# Try to normalize the unicode character as NFKC
|
||||
normalized = unicodedata.normalize('NFKC', chr(char))
|
||||
if normalized == char:
|
||||
# This is already in normalized form
|
||||
return
|
||||
|
||||
if len(normalized) == 1:
|
||||
normalized_char = ord(normalized)
|
||||
|
||||
if normalized_char not in self.mapping:
|
||||
# This is only needed to ensure that we get the normalized
|
||||
# forms for example, half-width Katakana characters are
|
||||
# normalized to their corresponding full width versions.
|
||||
# However, we don't want to overwrite existing mappings,
|
||||
# since something like Lowercase A with grave could be
|
||||
# normalized to lowercase A, which would break the translation
|
||||
self.mapping[normalized_char] = dst
|
||||
|
||||
with open(self.input_file, 'r', encoding='utf-8') as infile:
|
||||
for line in infile:
|
||||
src, dst = self.parse_line(line)
|
||||
if src is None:
|
||||
continue
|
||||
|
||||
self.mapping[src] = dst
|
||||
map_normalized(src, dst)
|
||||
|
||||
def build_extended_map(self):
|
||||
"""Build the extended map for every character in the BMP"""
|
||||
self.mapping[0] = 0 # Handle NUL
|
||||
for i in range(0x10000):
|
||||
# Iterate over the basic multilingual plane
|
||||
if i in self.mapping:
|
||||
continue
|
||||
|
||||
if 0xD800 <= i <= 0xDFFF:
|
||||
# UTF16 surrogate pairs - we want to mark it as a box character
|
||||
self.mapping[i] = self.REPLACEMENT_CHAR
|
||||
continue
|
||||
|
||||
normalized = unicodedata.normalize('NFKC', chr(i))
|
||||
if len(normalized) == 1:
|
||||
normalized_ord = ord(normalized)
|
||||
if normalized_ord in self.mapping:
|
||||
self.mapping[i] = self.mapping[normalized_ord]
|
||||
else:
|
||||
# No single character mapping exists
|
||||
self.mapping[i] = self.REPLACEMENT_CHAR
|
||||
|
||||
continue
|
||||
|
||||
# Check that all characters in the normalized are in the mapping table:
|
||||
sequence = []
|
||||
for c in normalized:
|
||||
if ord(c) in self.mapping:
|
||||
sequence.append(self.mapping[ord(c)])
|
||||
else:
|
||||
sequence.append(self.REPLACEMENT_CHAR)
|
||||
|
||||
# Check if it only contains the box character, or box char and space,
|
||||
# and reduce runs to a single instance
|
||||
if all(c in (self.REPLACEMENT_CHAR, self.mapping[0x20])
|
||||
for c in sequence):
|
||||
self.mapping[i] = self.REPLACEMENT_CHAR
|
||||
continue
|
||||
|
||||
sequence = tuple(sequence)
|
||||
if sequence not in self.sequences:
|
||||
if not self.sequences:
|
||||
last_sequence = 256
|
||||
else:
|
||||
last_sequence = max(self.sequences.values()) + 1
|
||||
|
||||
self.sequences[sequence] = last_sequence
|
||||
|
||||
self.mapping[i] = self.sequences[sequence]
|
||||
|
||||
def output_c_table(self, page_tuple, out_fd):
|
||||
"""Output the C table structure"""
|
||||
page_name = self.pages[page_tuple]
|
||||
|
||||
print(self.TABLE_FORMAT % (page_name), file=out_fd)
|
||||
|
||||
for i, val in enumerate(page_tuple):
|
||||
print(f"0x{val:02x}, ", end='', file=out_fd)
|
||||
if i % 8 == 7:
|
||||
print(f"// 0x{i-7:02x}-0x{i:02x}", file=out_fd)
|
||||
|
||||
print(self.TABLE_FOOTER, file=out_fd)
|
||||
|
||||
def build_tables(self):
|
||||
"""Build the C Tables"""
|
||||
with open(self.output_file, 'w', encoding='utf-8') as out_fd:
|
||||
print(self.HEADER, file=out_fd)
|
||||
|
||||
default_page = tuple([self.REPLACEMENT_CHAR] * 256)
|
||||
self.pages[default_page] = self.TABLE_NAME_DEFAULT
|
||||
self.output_c_table(default_page, out_fd)
|
||||
|
||||
for root_idx in range(256):
|
||||
base_idx = root_idx * 256
|
||||
page = [self.mapping[idx] for idx in range(base_idx, base_idx+256)]
|
||||
page_tuple = tuple(page)
|
||||
if page_tuple not in self.pages:
|
||||
page_name = self.TABLE_NAME_FORMAT % (root_idx)
|
||||
self.pages[page_tuple] = page_name
|
||||
self.output_c_table(page_tuple, out_fd)
|
||||
|
||||
self.root_table.append(self.pages[page_tuple])
|
||||
|
||||
print(self.TABLE_FORMAT % ('* root_table'), file=out_fd)
|
||||
|
||||
for page_id, page_name in enumerate(self.root_table):
|
||||
print(f" {page_name}, // 0x{page_id:02x}", file=out_fd)
|
||||
|
||||
print(self.TABLE_FOOTER, file=out_fd)
|
||||
|
||||
print(f"const uint8_t *sequence_table[{len(self.sequences)}] = {{", file=out_fd)
|
||||
for sequence, seq_id in self.sequences.items():
|
||||
seq_len = len(sequence)
|
||||
if seq_len >= 256:
|
||||
raise RuntimeError("Sequence way too long")
|
||||
|
||||
line = [f"0x{seq_len:02X}"]
|
||||
for seq_elem in sequence:
|
||||
line.append(f"0x{seq_elem:02X}")
|
||||
|
||||
line = ', '.join(line)
|
||||
print(f' [{seq_id-256}] = (const uint8_t[]){{ {line} }},', file=out_fd)
|
||||
|
||||
print(self.TABLE_FOOTER, file=out_fd)
|
||||
|
||||
def generate_test_tables(self):
|
||||
"""Build the test tables used by the test suite"""
|
||||
# Generate the expected output sequences for every table
|
||||
# Mapping is a dict mapping the code point as a string to the output
|
||||
# Sequence is a dict of <seq_tuple>:<seq_id> mappings (seq_id starts from 256)
|
||||
output = []
|
||||
sequences = [item[0] for item in sorted(self.sequences.items(),
|
||||
key=lambda item: item[1])]
|
||||
|
||||
# The mapping for the NUL byte (\x00) should be an empty sequence
|
||||
output.append([])
|
||||
|
||||
for i in range(1, 0x10000):
|
||||
seq = self.mapping[i]
|
||||
if seq >= 256:
|
||||
# Pull from sequence table
|
||||
seq = sequences[seq - 256]
|
||||
else:
|
||||
seq = [seq]
|
||||
output.append(seq)
|
||||
|
||||
# Find the longest length sequence (add 1 for the length byte)
|
||||
longest = max(len(seq) for seq in output) + 1
|
||||
# Find the next power of two that can hold this sequence
|
||||
if (longest & (longest - 1)) == 0:
|
||||
record_length = longest
|
||||
else:
|
||||
record_length = 1 << longest.bit_length()
|
||||
|
||||
with open(self.output_map, 'wb') as output_map:
|
||||
pad = [0] * record_length
|
||||
for seq in output:
|
||||
record = [len(seq)] + list(seq) + pad
|
||||
output_map.write(bytes(record[:record_length]))
|
||||
|
||||
if __name__ == "__main__":
|
||||
if len(sys.argv) != 3:
|
||||
sys.stderr.write('Usage: %s <input-map> <output-c-file>\n' %
|
||||
sys.argv[0])
|
||||
if len(sys.argv) != 4:
|
||||
sys.stderr.write(f"Usage: {sys.argv[0]} <input-map> <output-c-file> <output-json-map>\n")
|
||||
sys.exit(1)
|
||||
|
||||
with open(sys.argv[1], 'r') as infile:
|
||||
for line in infile:
|
||||
src, dst = parse_line(line)
|
||||
if src is not None:
|
||||
MapTable.add_to_table(src, dst)
|
||||
|
||||
with open(sys.argv[2], 'w') as outfile:
|
||||
outfile.write(AUTOGEN_HEADER % sys.argv[1])
|
||||
|
||||
for line in MapTable.output_table_as_list():
|
||||
outfile.write(line + '\n')
|
||||
BMPTable(sys.argv[1], sys.argv[2], sys.argv[3])
|
||||
|
|
|
|||
|
|
@ -11,11 +11,64 @@
|
|||
|
||||
#include "config.h"
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include "libx52util.h"
|
||||
#include "x52_char_map.h"
|
||||
|
||||
/**
|
||||
* @brief Converts a UTF8 stream to a uint32_t
|
||||
*
|
||||
* @param[in] utf8in Pointer to UTF8 input stream. Must be NUL-terminated
|
||||
* @param[out] unichr Output character pointer
|
||||
*
|
||||
* @returns number of bytes to advance stream by - 0 if NUL or input pointer is NULL
|
||||
*/
|
||||
static int utf8_to_u32(const uint8_t *utf8in, uint32_t *unichr)
|
||||
{
|
||||
uint8_t b;
|
||||
if (!utf8in || !*utf8in) return 0;
|
||||
|
||||
b = utf8in[0];
|
||||
|
||||
// 1-byte (0xxxxxxx)
|
||||
if (b < 0x80) {
|
||||
*unichr = b;
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Invalid leading bytes
|
||||
if (b < 0xC2 || b > 0xF4) goto error;
|
||||
|
||||
// 2-byte (110xxxxx 10xxxxxx)
|
||||
if ((b & 0xE0) == 0xC0) {
|
||||
if ((utf8in[1] & 0xC0) != 0x80) goto error;
|
||||
*unichr = ((b & 0x1F) << 6) | (utf8in[1] & 0x3F);
|
||||
return 2;
|
||||
}
|
||||
|
||||
// 3-byte (1110xxxx 10xxxxxx 10xxxxxx)
|
||||
if ((b & 0xF0) == 0xE0) {
|
||||
if ((utf8in[1] & 0xC0) != 0x80 || (utf8in[2] & 0xC0) != 0x80) goto error;
|
||||
*unichr = ((b & 0x0F) << 12) | ((utf8in[1] & 0x3F) << 6) | (utf8in[2] & 0x3F);
|
||||
return 3;
|
||||
}
|
||||
|
||||
// 4-byte (11110xxx 10xxxxxx 10xxxxxx 10xxxxxx)
|
||||
if ((b & 0xF8) == 0xF0) {
|
||||
if ((utf8in[1] & 0xC0) != 0x80 || (utf8in[2] & 0xC0) != 0x80 ||
|
||||
(utf8in[3] & 0xC0) != 0x80) goto error;
|
||||
*unichr = ((b & 0x07) << 18) | ((utf8in[1] & 0x3F) << 12) |
|
||||
((utf8in[2] & 0x3F) << 6) | (utf8in[3] & 0x3F);
|
||||
return 4;
|
||||
}
|
||||
|
||||
error:
|
||||
*unichr = 0xFFFD; // Unicode Replacement Character
|
||||
return 1; // Consume lead byte to attempt resync
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Convert UTF8 string to X52 character map.
|
||||
*
|
||||
|
|
@ -32,48 +85,60 @@
|
|||
int libx52util_convert_utf8_string(const uint8_t *input,
|
||||
uint8_t *output, size_t *len)
|
||||
{
|
||||
struct map_entry *entry;
|
||||
size_t index;
|
||||
int retval = 0;
|
||||
unsigned char local_index;
|
||||
uint32_t unichr;
|
||||
int bytes_consumed;
|
||||
uint16_t translated;
|
||||
|
||||
if (!input || !output || !len || !*len) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
index = 0;
|
||||
entry = &map_root[*input];
|
||||
// Reset the output array
|
||||
memset(output, 0, *len);
|
||||
|
||||
while (*input) {
|
||||
input++;
|
||||
if (entry->type == TYPE_ENTRY) {
|
||||
output[index] = entry->value;
|
||||
// Length check
|
||||
if (index >= *len) {
|
||||
retval = -E2BIG;
|
||||
break;
|
||||
}
|
||||
|
||||
bytes_consumed = utf8_to_u32(input, &unichr);
|
||||
if (bytes_consumed == 0) {
|
||||
// We should never get here, since the while loop should have
|
||||
// caught it
|
||||
retval = 0;
|
||||
break;
|
||||
}
|
||||
input += bytes_consumed;
|
||||
|
||||
// Check for bytes in the Supplementary planes
|
||||
if (unichr >= 0x10000) {
|
||||
unichr = 0xFFFD; // Unicode replacement character
|
||||
}
|
||||
|
||||
translated = root_table[unichr >> 8][unichr & 0xFF];
|
||||
if (translated < 256) {
|
||||
// Table entry, push to output
|
||||
output[index] = (uint8_t)translated;
|
||||
index++;
|
||||
if (index >= *len) {
|
||||
} else {
|
||||
// We have a sequence, output that
|
||||
const uint8_t *sequence = sequence_table[translated - 256];
|
||||
uint8_t seq_len = sequence[0];
|
||||
|
||||
// Let's make sure that we can actually output to the buffer
|
||||
if ((index + seq_len) >= *len) {
|
||||
retval = -E2BIG;
|
||||
break;
|
||||
}
|
||||
entry = &map_root[*input];
|
||||
} else if (entry->type == TYPE_POINTER) {
|
||||
local_index = *input;
|
||||
if (local_index < 0x80 || local_index >= 0xC0) {
|
||||
/* Invalid input, skip till we find the start of another
|
||||
* valid UTF-8 character
|
||||
*/
|
||||
while (*input >= 0x80 && *input < 0xC0) {
|
||||
input++; /* Skip invalid characters */
|
||||
}
|
||||
|
||||
/* New UTF-8 character, reset the entry pointer */
|
||||
entry = &map_root[*input];
|
||||
} else {
|
||||
/* Mask off the upper bits, we only care about the lower 6 bits */
|
||||
local_index &= 0x3F;
|
||||
entry = &(entry->next[local_index]);
|
||||
}
|
||||
} else {
|
||||
/* Invalid value, skip */
|
||||
while (*input >= 0x80 && *input < 0xC0) {
|
||||
input++; /* Skip invalid characters */
|
||||
for (int i = 1; i <= seq_len; i++) {
|
||||
output[index] = sequence[i];
|
||||
index++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -81,4 +146,3 @@ int libx52util_convert_utf8_string(const uint8_t *input,
|
|||
*len = index;
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,215 @@
|
|||
/*
|
||||
* X52 character map lookup test
|
||||
*
|
||||
* Copyright (C) 2026 Nirenjan Krishnan <nirenjan@nirenjan.org>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-only WITH Classpath-exception-2.0
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/mman.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "libx52util.h"
|
||||
|
||||
// Fix this if we ever hit longer sequences
|
||||
#define RECORD_SIZE 8
|
||||
|
||||
// Blindly encode a string into it's smallest UTF8 representation
|
||||
static void encode_utf8(uint32_t cp, uint8_t *out)
|
||||
{
|
||||
if (cp <= 0x7F) {
|
||||
out[0] = (uint8_t)cp;
|
||||
} else if (cp <= 0x7FF) {
|
||||
out[0] = (uint8_t)(0xC0 | (cp >> 6));
|
||||
out[1] = (uint8_t)(0x80 | (cp & 0x3F));
|
||||
} else if (cp <= 0xFFFF) {
|
||||
out[0] = (uint8_t)(0xE0 | (cp >> 12));
|
||||
out[1] = (uint8_t)(0x80 | ((cp >> 6) & 0x3F));
|
||||
out[2] = (uint8_t)(0x80 | (cp & 0x3F));
|
||||
} else if (cp <= 0x1FFFFF) {
|
||||
out[0] = (uint8_t)(0xF0 | (cp >> 18));
|
||||
out[1] = (uint8_t)(0x80 | ((cp >> 12) & 0x3F));
|
||||
out[2] = (uint8_t)(0x80 | ((cp >> 6) & 0x3F));
|
||||
out[3] = (uint8_t)(0x80 | (cp & 0x3F));
|
||||
} else if (cp <= 0x3FFFFFF) {
|
||||
out[0] = (uint8_t)(0xF8 | (cp >> 24));
|
||||
out[1] = (uint8_t)(0x80 | ((cp >> 18) & 0x3F));
|
||||
out[2] = (uint8_t)(0x80 | ((cp >> 12) & 0x3F));
|
||||
out[3] = (uint8_t)(0x80 | ((cp >> 6) & 0x3F));
|
||||
out[4] = (uint8_t)(0x80 | (cp & 0x3F));
|
||||
} else if (cp <= 0x7FFFFFFF) {
|
||||
out[0] = (uint8_t)(0xFC | (cp >> 30));
|
||||
out[1] = (uint8_t)(0x80 | ((cp >> 24) & 0x3F));
|
||||
out[2] = (uint8_t)(0x80 | ((cp >> 18) & 0x3F));
|
||||
out[3] = (uint8_t)(0x80 | ((cp >> 12) & 0x3F));
|
||||
out[4] = (uint8_t)(0x80 | ((cp >> 6) & 0x3F));
|
||||
out[5] = (uint8_t)(0x80 | (cp & 0x3F));
|
||||
} else { // 0x80000000 to 0xFFFFFFFF (7 bytes)
|
||||
out[0] = (uint8_t)0xFE; // Binary 11111110
|
||||
out[1] = (uint8_t)(0x80 | ((cp >> 30) & 0x3F));
|
||||
out[2] = (uint8_t)(0x80 | ((cp >> 24) & 0x3F));
|
||||
out[3] = (uint8_t)(0x80 | ((cp >> 18) & 0x3F));
|
||||
out[4] = (uint8_t)(0x80 | ((cp >> 12) & 0x3F));
|
||||
out[5] = (uint8_t)(0x80 | ((cp >> 6) & 0x3F));
|
||||
out[6] = (uint8_t)(0x80 | (cp & 0x3F));
|
||||
}
|
||||
}
|
||||
|
||||
static double get_time_diff(struct timespec start, struct timespec end)
|
||||
{
|
||||
return (end.tv_sec - start.tv_sec) + (end.tv_nsec - start.tv_nsec) / 1e9;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
uint8_t input[8] = {0};
|
||||
uint8_t output[RECORD_SIZE];
|
||||
size_t len;
|
||||
int result;
|
||||
|
||||
int fd;
|
||||
uint8_t *expected_blob;
|
||||
bool smp_pages_ok;
|
||||
|
||||
struct timespec start, end;
|
||||
|
||||
// Argument check
|
||||
if (argc != 2) {
|
||||
puts("Bail out! Invalid number of arguments");
|
||||
puts("# Usage: libx52util-bmp-test <path-to-bin>");
|
||||
return 1;
|
||||
}
|
||||
|
||||
fd = open(argv[1], O_RDONLY);
|
||||
if (fd < 0) {
|
||||
printf("Bail out! Error %d opening bin file %s: %s\n",
|
||||
errno, argv[1], strerror(errno));
|
||||
return 1;
|
||||
}
|
||||
|
||||
expected_blob = mmap(NULL, 0x10000 * RECORD_SIZE,
|
||||
PROT_READ, MAP_SHARED, fd, 0);
|
||||
if (expected_blob == MAP_FAILED) {
|
||||
printf("Bail out! MMAP failed with error %d: %s\n",
|
||||
errno, strerror(errno));
|
||||
}
|
||||
|
||||
puts("TAP version 13");
|
||||
// Check the 256 BMP Pages, plus the supplementary pages
|
||||
puts("1..257");
|
||||
|
||||
clock_gettime(CLOCK_MONOTONIC, &start);
|
||||
|
||||
for (uint32_t page = 0; page < 256; page++) {
|
||||
bool page_ok = true;
|
||||
|
||||
for (uint32_t offset = 0; offset < 256; offset++) {
|
||||
uint32_t cp = page * 256 + offset;
|
||||
const uint8_t *rec = &expected_blob[cp * RECORD_SIZE];
|
||||
|
||||
memset(input, 0, sizeof(input));
|
||||
memset(output, 0, sizeof(output));
|
||||
encode_utf8(cp, input);
|
||||
len = sizeof(output);
|
||||
|
||||
result = libx52util_convert_utf8_string(input, output, &len);
|
||||
if (result != 0) {
|
||||
page_ok = false;
|
||||
printf("# Bad result @ %04X: %d\n", cp, result);
|
||||
break;
|
||||
}
|
||||
|
||||
// result is OK, check against the expected blob
|
||||
if (len != rec[0]) {
|
||||
page_ok = false;
|
||||
printf("# Length mismatch @ %04X: expected %u, got %zu\n",
|
||||
cp, rec[0], len);
|
||||
break;
|
||||
}
|
||||
|
||||
// Length is OK, check the bytes
|
||||
if (memcmp(output, &rec[1], rec[0]) != 0) {
|
||||
page_ok = false;
|
||||
printf("# Output mismatch @ %04X:\n", cp);
|
||||
printf("# exp/got:");
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
printf("%02X/%02X ", rec[i+1], output[i]);
|
||||
}
|
||||
puts("");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
printf("%sok - %d Page 0x%02x\n", page_ok ? "": "not ",
|
||||
page + 1, page);
|
||||
}
|
||||
|
||||
clock_gettime(CLOCK_MONOTONIC, &end);
|
||||
{
|
||||
double time_spent = get_time_diff(start, end);
|
||||
printf("# -- Benchmark results --\n");
|
||||
printf("# Total time for 64K lookups: %.4f seconds\n", time_spent);
|
||||
printf("# Throughput: %.2f Mchars/sec\n", (65536.0 / time_spent) / 1e6);
|
||||
|
||||
printf("# -----------------------\n");
|
||||
}
|
||||
|
||||
// Handle the supplementary pages
|
||||
smp_pages_ok = true;
|
||||
for (uint32_t smp = 0x1; smp <= 0x10; smp++) {
|
||||
const uint8_t *rec = &expected_blob[0xFFFD * RECORD_SIZE];
|
||||
for (uint32_t offset = 0; offset < 0x100; offset += 0xFF) {
|
||||
uint32_t cp = smp * 0x10000 + offset;
|
||||
|
||||
memset(input, 0, sizeof(input));
|
||||
memset(output, 0, sizeof(output));
|
||||
len = sizeof(output);
|
||||
encode_utf8(cp, input);
|
||||
|
||||
result = libx52util_convert_utf8_string(input, output, &len);
|
||||
if (result != 0) {
|
||||
smp_pages_ok = false;
|
||||
printf("# Bad result @ %08X: %d\n", cp, result);
|
||||
break;
|
||||
}
|
||||
|
||||
// result is OK, check against the expected blob
|
||||
if (len != rec[0]) {
|
||||
smp_pages_ok = false;
|
||||
printf("# Length mismatch @ %08X: expected %u, got %zu\n",
|
||||
|
||||
cp, rec[0], len);
|
||||
break;
|
||||
}
|
||||
|
||||
// Length is OK, check the bytes
|
||||
if (memcmp(output, &rec[1], rec[0]) != 0) {
|
||||
smp_pages_ok = false;
|
||||
printf("# Output mismatch @ %08X:\n", cp);
|
||||
printf("# exp/got:");
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
printf("%02X/%02X ", rec[i+1], output[i]);
|
||||
}
|
||||
puts("");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!smp_pages_ok) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
printf("%sok - 257 SMP tests\n", smp_pages_ok ? "" : "not ");
|
||||
|
||||
// Cleanup
|
||||
munmap(expected_blob, 0x10000 * RECORD_SIZE);
|
||||
close(fd);
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -0,0 +1,149 @@
|
|||
project('libx52', 'C',
|
||||
license: 'GPL-2.0-only WITH Classpath-exception-2.0',
|
||||
version: '0.3.3',
|
||||
meson_version: '>=0.61')
|
||||
|
||||
dep_libusb = dependency('libusb-1.0', required: true)
|
||||
dep_hidapi = dependency('hidapi-hidraw', required: false)
|
||||
if not dep_hidapi.found()
|
||||
dep_hidapi = dependency('hidapi', required: true)
|
||||
endif
|
||||
|
||||
dep_evdev = dependency('libevdev', required: false)
|
||||
|
||||
dep_systemd = dependency('systemd', required: false)
|
||||
dep_udev = dependency('udev', required: false)
|
||||
|
||||
dep_cmocka = dependency('cmocka', required: false)
|
||||
if not dep_cmocka.found()
|
||||
dep_cmocka = disabler()
|
||||
endif
|
||||
|
||||
doxygen_program = find_program('doxygen', required: false)
|
||||
|
||||
# pkgconfig module is needed
|
||||
pkgconfig = import('pkgconfig')
|
||||
|
||||
# Python 3.5 or greater is needed
|
||||
pymod = import('python')
|
||||
python = pymod.find_installation('python3')
|
||||
pyversion = python.language_version().split('.')
|
||||
assert(pyversion[1].to_int() >= 5, 'Require Python >= 3.5')
|
||||
|
||||
#######################################################################
|
||||
# config.h
|
||||
#######################################################################
|
||||
compiler = meson.get_compiler('c')
|
||||
cdata = configuration_data()
|
||||
cdata.set_quoted('PACKAGE', meson.project_name())
|
||||
cdata.set_quoted('PACKAGE_BUGREPORT', 'https://github.com/nirenjan/libx52/issues')
|
||||
cdata.set_quoted('PACKAGE_NAME', meson.project_name())
|
||||
cdata.set_quoted('LOCALEDIR', get_option('prefix') / get_option('localedir'))
|
||||
cdata.set_quoted('SYSCONFDIR', get_option('prefix') / get_option('sysconfdir'))
|
||||
cdata.set_quoted('LOCALSTATEDIR', get_option('prefix') / get_option('localstatedir'))
|
||||
cdata.set_quoted('PACKAGE_VERSION', meson.project_version())
|
||||
cdata.set_quoted('VERSION', meson.project_version())
|
||||
cdata.set10('ENABLE_NLS', not get_option('nls').disabled())
|
||||
cdata.set10('HAVE_FUNC_ATTRIBUTE_NORETURN', compiler.has_function_attribute('noreturn'))
|
||||
cdata.set10('HAVE_STRUCT_TM_TM_GMTOFF',
|
||||
compiler.has_member('struct tm', 'tm_gmtoff', prefix:'#include <time.h>'))
|
||||
|
||||
config_h = configure_file(
|
||||
input: 'config.h.meson',
|
||||
output: 'config.h',
|
||||
configuration: cdata
|
||||
)
|
||||
|
||||
#######################################################################
|
||||
# Internationalization
|
||||
#######################################################################
|
||||
i18n = import('i18n')
|
||||
dep_intl = dependency('intl', required: false)
|
||||
if not dep_intl.found() and host_machine.system() == 'darwin'
|
||||
brew = find_program('brew', required: false)
|
||||
if brew.found()
|
||||
brew_prefix = run_command(brew, '--prefix', 'gettext',
|
||||
check: true).stdout().strip()
|
||||
|
||||
brew_inc = brew_prefix / 'include'
|
||||
brew_lib_dir = brew_prefix / 'lib'
|
||||
|
||||
dep_intl = compiler.find_library('intl', dirs: brew_lib_dir)
|
||||
dep_intl = declare_dependency(dependencies: dep_intl, include_directories: brew_inc)
|
||||
endif
|
||||
endif
|
||||
|
||||
# # define GETTEXT_PACKAGE
|
||||
if not get_option('nls').disabled()
|
||||
add_project_arguments(
|
||||
'-DGETTEXT_PACKAGE="@0@"'.format(meson.project_name()),
|
||||
language:'C')
|
||||
|
||||
subdir('po')
|
||||
endif
|
||||
|
||||
add_project_arguments('-isystem', meson.current_source_dir() / 'sys', language: 'C')
|
||||
|
||||
#######################################################################
|
||||
# Internal dependencies
|
||||
#######################################################################
|
||||
|
||||
# pinelog
|
||||
pinelog_options = []
|
||||
if dep_systemd.found() and not get_option('systemd-logs').disabled()
|
||||
# If systemd logs is enabled or auto, and systemd is found, then hide
|
||||
# the timestamps in log messages
|
||||
pinelog_options = ['show-date=false']
|
||||
endif
|
||||
|
||||
sub_pinelog = subproject('pinelog', required: true,
|
||||
default_options: pinelog_options)
|
||||
dep_pinelog = sub_pinelog.get_variable('libpinelog_dep')
|
||||
|
||||
# inih
|
||||
# Use system inih
|
||||
dep_inih = dependency('inih')
|
||||
|
||||
#######################################################################
|
||||
# Shared libraries and programs
|
||||
#######################################################################
|
||||
# Includes
|
||||
includes = include_directories('.', 'libx52', 'libx52io', 'libx52util')
|
||||
|
||||
subdir('libx52')
|
||||
subdir('libx52io')
|
||||
subdir('libx52util')
|
||||
subdir('bugreport')
|
||||
subdir('cli')
|
||||
subdir('joytest')
|
||||
subdir('evtest')
|
||||
subdir('daemon')
|
||||
subdir('udev')
|
||||
|
||||
#######################################################################
|
||||
# Documentation - doxygen
|
||||
#######################################################################
|
||||
if doxygen_program.found()
|
||||
doxyfile = configure_file(
|
||||
input: 'Doxyfile.in',
|
||||
output: 'Doxyfile',
|
||||
configuration: {
|
||||
'PACKAGE_NAME': meson.project_name(),
|
||||
'PACKAGE_VERSION': meson.project_version(),
|
||||
'abs_top_builddir': meson.project_build_root(),
|
||||
'abs_top_srcdir': meson.project_source_root(),
|
||||
}
|
||||
)
|
||||
|
||||
custom_target('docs',
|
||||
depend_files: [doxyfile, 'DoxygenLayout.xml'],
|
||||
command: [doxygen_program],
|
||||
output: 'docs'
|
||||
)
|
||||
|
||||
meson.add_install_script(
|
||||
'install-doxygen-docs.sh',
|
||||
get_option('datadir') / 'doc' / meson.project_name(),
|
||||
get_option('mandir'),
|
||||
)
|
||||
endif
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
option('systemd-logs',
|
||||
type: 'feature',
|
||||
description: 'Hide timestamps in log messages, needed for systemd')
|
||||
|
||||
option('systemd-unit-dir',
|
||||
type: 'string',
|
||||
value: '',
|
||||
description: 'Directory for systemd service files (leave empty for auto-detection)')
|
||||
|
||||
option('nls',
|
||||
type: 'feature',
|
||||
description: 'Enable message translations')
|
||||
|
||||
option('udev-rules-dir',
|
||||
type: 'string',
|
||||
value: '',
|
||||
description: 'Directory for udev rules (leave empty for auto-detection)')
|
||||
|
||||
option('input-group',
|
||||
type: 'string', value: 'plugdev',
|
||||
description: 'Group for input devices')
|
||||
205
po/libx52.pot
205
po/libx52.pot
|
|
@ -6,9 +6,9 @@
|
|||
#, fuzzy
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: libx52 0.3.0\n"
|
||||
"Project-Id-Version: libx52 0.3.3\n"
|
||||
"Report-Msgid-Bugs-To: https://github.com/nirenjan/libx52/issues\n"
|
||||
"POT-Creation-Date: 2022-12-25 22:03-0800\n"
|
||||
"POT-Creation-Date: 2026-03-19 00:09-0700\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
|
|
@ -233,26 +233,26 @@ msgstr ""
|
|||
msgid "Read timeout"
|
||||
msgstr ""
|
||||
|
||||
#: evtest/ev_test.c:109
|
||||
#: evtest/ev_test.c:110
|
||||
#, c-format
|
||||
msgid "Device ID: vendor 0x%04x product 0x%04x version 0x%04x\n"
|
||||
msgstr ""
|
||||
|
||||
#: evtest/ev_test.c:113
|
||||
#: evtest/ev_test.c:114
|
||||
#, c-format
|
||||
msgid "Device name: \"%s %s\"\n"
|
||||
msgstr ""
|
||||
|
||||
#: evtest/ev_test.c:116
|
||||
#: evtest/ev_test.c:117
|
||||
#, c-format
|
||||
msgid "Serial number: \"%s\"\n"
|
||||
msgstr ""
|
||||
|
||||
#: evtest/ev_test.c:117
|
||||
#: evtest/ev_test.c:118
|
||||
msgid "Testing (interrupt to exit)\n"
|
||||
msgstr ""
|
||||
|
||||
#: evtest/ev_test.c:157 evtest/ev_test.c:165
|
||||
#: evtest/ev_test.c:158 evtest/ev_test.c:166
|
||||
#, c-format
|
||||
msgid "Event @ %ld.%06ld: %s, value %d\n"
|
||||
msgstr ""
|
||||
|
|
@ -493,17 +493,17 @@ msgstr ""
|
|||
msgid "OK"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/x52d_main.c:64
|
||||
#: daemon/x52d_main.c:67
|
||||
#, c-format
|
||||
msgid "Error %d setting log file: %s\n"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/x52d_main.c:80
|
||||
#: daemon/x52d_main.c:83
|
||||
#, c-format
|
||||
msgid "Error %d installing handler for signal %d: %s"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/x52d_main.c:91
|
||||
#: daemon/x52d_main.c:94
|
||||
#, c-format
|
||||
msgid ""
|
||||
"Usage: %s [-f] [-v] [-q]\n"
|
||||
|
|
@ -513,112 +513,112 @@ msgid ""
|
|||
"\t[-b notify-socket-path]\n"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/x52d_main.c:124
|
||||
#: daemon/x52d_main.c:129
|
||||
#, c-format
|
||||
msgid "Daemon is already running as PID %u"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/x52d_main.c:266
|
||||
#: daemon/x52d_main.c:271
|
||||
#, c-format
|
||||
msgid "Unable to parse configuration override '%s'\n"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/x52d_main.c:298
|
||||
#: daemon/x52d_main.c:303
|
||||
#, c-format
|
||||
msgid "Foreground = %s"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/x52d_main.c:298 daemon/x52d_main.c:299
|
||||
#: daemon/x52d_main.c:303 daemon/x52d_main.c:304
|
||||
msgid "true"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/x52d_main.c:298 daemon/x52d_main.c:299
|
||||
#: daemon/x52d_main.c:303 daemon/x52d_main.c:304
|
||||
msgid "false"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/x52d_main.c:299
|
||||
#, c-format
|
||||
msgid "Quiet = %s"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/x52d_main.c:300
|
||||
#, c-format
|
||||
msgid "Verbosity = %d"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/x52d_main.c:301
|
||||
#, c-format
|
||||
msgid "Log file = %s"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/x52d_main.c:302
|
||||
#, c-format
|
||||
msgid "Config file = %s"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/x52d_main.c:303
|
||||
#, c-format
|
||||
msgid "PID file = %s"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/x52d_main.c:304
|
||||
#, c-format
|
||||
msgid "Command socket = %s"
|
||||
msgid "Quiet = %s"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/x52d_main.c:305
|
||||
#, c-format
|
||||
msgid "Verbosity = %d"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/x52d_main.c:306
|
||||
#, c-format
|
||||
msgid "Log file = %s"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/x52d_main.c:307
|
||||
#, c-format
|
||||
msgid "Config file = %s"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/x52d_main.c:308
|
||||
#, c-format
|
||||
msgid "PID file = %s"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/x52d_main.c:309
|
||||
#, c-format
|
||||
msgid "Command socket = %s"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/x52d_main.c:310
|
||||
#, c-format
|
||||
msgid "Notify socket = %s"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/x52d_main.c:316
|
||||
#: daemon/x52d_main.c:321
|
||||
#, c-format
|
||||
msgid "Error %d blocking signals on child threads: %s"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/x52d_main.c:335
|
||||
#: daemon/x52d_main.c:340
|
||||
#, c-format
|
||||
msgid "Error %d unblocking signals on child threads: %s"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/x52d_main.c:348
|
||||
#: daemon/x52d_main.c:353
|
||||
msgid "Reloading X52 configuration"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/x52d_main.c:355
|
||||
#: daemon/x52d_main.c:360
|
||||
msgid "Saving X52 configuration to disk"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/x52d_main.c:361
|
||||
#: daemon/x52d_main.c:366
|
||||
#, c-format
|
||||
msgid "Received termination signal %s"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/x52d_main.c:378
|
||||
#: daemon/x52d_main.c:383
|
||||
msgid "Shutting down X52 daemon"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/x52d_client.c:32
|
||||
#: daemon/x52d_client.c:37
|
||||
#, c-format
|
||||
msgid "Error accepting client connection on socket fd %d: %s"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/x52d_client.c:38
|
||||
#: daemon/x52d_client.c:43
|
||||
#, c-format
|
||||
msgid "Error marking client fd %d as nonblocking: %s"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/x52d_client.c:87
|
||||
#: daemon/x52d_client.c:97
|
||||
#, c-format
|
||||
msgid "Error when polling socket: FD %d, error %d, len %lu"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/x52d_client.c:118
|
||||
#: daemon/x52d_client.c:127
|
||||
#, c-format
|
||||
msgid "Error %d when polling %d descriptors: %s"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/x52d_client.c:121
|
||||
#: daemon/x52d_client.c:130
|
||||
msgid "Timed out when polling"
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -627,7 +627,7 @@ msgstr ""
|
|||
msgid "Setting clock enable to %s"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/x52d_clock.c:35 daemon/x52d_clock.c:116
|
||||
#: daemon/x52d_clock.c:35 daemon/x52d_clock.c:119
|
||||
#, c-format
|
||||
msgid "Setting %s clock timezone to %s"
|
||||
msgstr ""
|
||||
|
|
@ -648,79 +648,69 @@ msgstr ""
|
|||
msgid "Unable to backup timezone environment. Falling back to UTC"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/x52d_clock.c:135
|
||||
#: daemon/x52d_clock.c:138
|
||||
#, c-format
|
||||
msgid "Setting %s clock format to %s"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/x52d_clock.c:159
|
||||
#: daemon/x52d_clock.c:162
|
||||
#, c-format
|
||||
msgid "Setting date format to %s"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/x52d_clock.c:170
|
||||
#: daemon/x52d_clock.c:174
|
||||
msgid "Starting X52 clock manager thread"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/x52d_clock.c:181
|
||||
#: daemon/x52d_clock.c:185
|
||||
#, c-format
|
||||
msgid "Error %d retrieving current time: %s"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/x52d_clock.c:202
|
||||
#: daemon/x52d_clock.c:206
|
||||
#, c-format
|
||||
msgid "Error %d initializing clock thread: %s"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/x52d_clock.c:209
|
||||
#: daemon/x52d_clock.c:213
|
||||
msgid "Shutting down X52 clock manager thread"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/x52d_command.c:367
|
||||
#: daemon/x52d_command.c:379
|
||||
#, c-format
|
||||
msgid "Error reading from client %d: %s"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/x52d_command.c:378
|
||||
#: daemon/x52d_command.c:390
|
||||
#, c-format
|
||||
msgid "Short write to client %d; expected %d bytes, wrote %d bytes"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/x52d_command.c:402
|
||||
#: daemon/x52d_command.c:415
|
||||
#, c-format
|
||||
msgid "Error %d during command loop: %s"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/x52d_command.c:430
|
||||
#: daemon/x52d_command.c:442
|
||||
#, c-format
|
||||
msgid "Error creating command socket: %s"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/x52d_command.c:437
|
||||
#: daemon/x52d_command.c:450
|
||||
#, c-format
|
||||
msgid "Error getting command socket flags: %s"
|
||||
msgid "Error marking command socket as nonblocking: %s"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/x52d_command.c:441
|
||||
#, c-format
|
||||
msgid "Error setting command socket flags: %s"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/x52d_command.c:449
|
||||
#, c-format
|
||||
msgid "Error binding to command socket: %s"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/x52d_command.c:454
|
||||
#: daemon/x52d_command.c:456
|
||||
#, c-format
|
||||
msgid "Error listening on command socket: %s"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/x52d_command.c:464
|
||||
#: daemon/x52d_command.c:460
|
||||
msgid "Starting command processing thread"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/x52d_command.c:482
|
||||
#: daemon/x52d_command.c:478
|
||||
msgid "Shutting down command processing thread"
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -782,67 +772,67 @@ msgstr ""
|
|||
msgid "Error processing override '%s.%s=%s'"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/x52d_device.c:36
|
||||
#: daemon/x52d_device.c:37
|
||||
msgid "Starting X52 device manager thread"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/x52d_device.c:43
|
||||
#: daemon/x52d_device.c:44
|
||||
#, c-format
|
||||
msgid "Error %d connecting to device: %s"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/x52d_device.c:52
|
||||
#: daemon/x52d_device.c:53
|
||||
msgid "Device connected, writing configuration"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/x52d_device.c:75
|
||||
#: daemon/x52d_device.c:76
|
||||
msgid "Initializing libx52"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/x52d_device.c:79
|
||||
#: daemon/x52d_device.c:80
|
||||
#, c-format
|
||||
msgid "Failure %d initializing libx52: %s"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/x52d_device.c:90
|
||||
#: daemon/x52d_device.c:91
|
||||
msgid "Shutting down X52 device manager thread"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/x52d_device.c:103
|
||||
#: daemon/x52d_device.c:104
|
||||
#, c-format
|
||||
msgid "Error %d when updating X52 parameter: %s"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/x52d_device.c:179
|
||||
#: daemon/x52d_device.c:180
|
||||
#, c-format
|
||||
msgid "Error %d when updating X52 device: %s"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/x52d_io.c:42
|
||||
#: daemon/x52d_io.c:43
|
||||
msgid "Starting X52 I/O thread"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/x52d_io.c:64
|
||||
#: daemon/x52d_io.c:65
|
||||
#, c-format
|
||||
msgid "Error %d opening X52 I/O device: %s"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/x52d_io.c:75
|
||||
#: daemon/x52d_io.c:76
|
||||
#, c-format
|
||||
msgid "Error %d reading from X52 I/O device: %s"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/x52d_io.c:102
|
||||
#: daemon/x52d_io.c:103
|
||||
#, c-format
|
||||
msgid "Error %d initializing X52 I/O library: %s"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/x52d_io.c:108
|
||||
#: daemon/x52d_io.c:109
|
||||
#, c-format
|
||||
msgid "Error %d initializing I/O driver thread: %s"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/x52d_io.c:115
|
||||
#: daemon/x52d_io.c:116
|
||||
msgid "Shutting down X52 I/O driver thread"
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -885,24 +875,24 @@ msgstr ""
|
|||
msgid "Error writing mouse sync event"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/x52d_mouse_evdev.c:134
|
||||
#: daemon/x52d_mouse_evdev.c:135
|
||||
msgid "Starting X52 virtual mouse driver thread"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/x52d_mouse_evdev.c:157
|
||||
#: daemon/x52d_mouse_evdev.c:158
|
||||
#, c-format
|
||||
msgid "Error %d initializing mouse thread: %s"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/x52d_mouse_evdev.c:164
|
||||
#: daemon/x52d_mouse_evdev.c:165
|
||||
msgid "Shutting down X52 virtual mouse driver thread"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/x52d_mouse_evdev.c:171
|
||||
#: daemon/x52d_mouse_evdev.c:172
|
||||
msgid "Virtual mouse not created. Ignoring thread state change"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/x52d_mouse_evdev.c:236
|
||||
#: daemon/x52d_mouse_evdev.c:237
|
||||
#, c-format
|
||||
msgid "Error %d creating X52 virtual mouse: %s"
|
||||
msgstr ""
|
||||
|
|
@ -917,41 +907,36 @@ msgstr ""
|
|||
msgid "Error marking notification socket as nonblocking: %s"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/x52d_notify.c:59
|
||||
#, c-format
|
||||
msgid "Error binding to notification socket: %s"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/x52d_notify.c:64
|
||||
#: daemon/x52d_notify.c:56
|
||||
#, c-format
|
||||
msgid "Error listening on notification socket: %s"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/x52d_notify.c:73
|
||||
#: daemon/x52d_notify.c:65
|
||||
msgid "Error setting up notification socket"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/x52d_notify.c:90 daemon/x52d_notify.c:106
|
||||
#: daemon/x52d_notify.c:81 daemon/x52d_notify.c:91
|
||||
#, c-format
|
||||
msgid "Error %d reading from pipe: %s"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/x52d_notify.c:148
|
||||
#: daemon/x52d_notify.c:128
|
||||
#, c-format
|
||||
msgid "Error %d writing notification pipe: %s"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/x52d_notify.c:193
|
||||
#: daemon/x52d_notify.c:174
|
||||
#, c-format
|
||||
msgid "Error %d creating notification pipe: %s"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/x52d_notify.c:202
|
||||
#: daemon/x52d_notify.c:183
|
||||
#, c-format
|
||||
msgid "Error %d initializing notify thread: %s"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/x52d_notify.c:208
|
||||
#: daemon/x52d_notify.c:189
|
||||
#, c-format
|
||||
msgid "Error %d initializing notify listener: %s"
|
||||
msgstr ""
|
||||
|
|
@ -966,7 +951,7 @@ msgstr ""
|
|||
msgid "Argument length too long\n"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/x52ctl.c:142
|
||||
#: daemon/x52ctl.c:180
|
||||
#, c-format
|
||||
msgid "Running in interactive mode, ignoring extra arguments\n"
|
||||
msgstr ""
|
||||
|
|
|
|||
|
|
@ -0,0 +1,14 @@
|
|||
i18n.gettext(meson.project_name(),
|
||||
args: [
|
||||
'--directory=' + meson.project_source_root(),
|
||||
'--msgid-bugs-address=https://github.com/nirenjan/libx52/issues',
|
||||
'--package-name=' + meson.project_name(),
|
||||
'--package-version=' + meson.project_version(),
|
||||
'--copyright-holder=Nirenjan Krishnan',
|
||||
'--keyword=_', '--keyword=N_',
|
||||
],
|
||||
languages: [
|
||||
'xx_PL',
|
||||
],
|
||||
install: true,
|
||||
)
|
||||
169
po/xx_PL.po
169
po/xx_PL.po
|
|
@ -7,8 +7,8 @@ msgid ""
|
|||
msgstr ""
|
||||
"Project-Id-Version: libx52 0.2.3\n"
|
||||
"Report-Msgid-Bugs-To: https://github.com/nirenjan/libx52/issues\n"
|
||||
"POT-Creation-Date: 2022-12-25 22:03-0800\n"
|
||||
"PO-Revision-Date: 2022-09-22 21:23-0700\n"
|
||||
"POT-Creation-Date: 2026-03-19 00:09-0700\n"
|
||||
"PO-Revision-Date: 2023-01-04 08:40-0800\n"
|
||||
"Last-Translator: Nirenjan Krishnan <nirenjan@gmail.com>\n"
|
||||
"Language-Team: Dummy Language for testing i18n\n"
|
||||
"Language: xx_PL\n"
|
||||
|
|
@ -233,26 +233,26 @@ msgstr "I/O erroray"
|
|||
msgid "Read timeout"
|
||||
msgstr "Eadray imeouttay"
|
||||
|
||||
#: evtest/ev_test.c:109
|
||||
#: evtest/ev_test.c:110
|
||||
#, c-format
|
||||
msgid "Device ID: vendor 0x%04x product 0x%04x version 0x%04x\n"
|
||||
msgstr "Eviceday IDay: endorvay 0x%04x oductpray 0x%04x ersionvay 0x%04x\n"
|
||||
|
||||
#: evtest/ev_test.c:113
|
||||
#: evtest/ev_test.c:114
|
||||
#, c-format
|
||||
msgid "Device name: \"%s %s\"\n"
|
||||
msgstr "Eviceday amenay: \"%s %s\"\n"
|
||||
|
||||
#: evtest/ev_test.c:116
|
||||
#: evtest/ev_test.c:117
|
||||
#, c-format
|
||||
msgid "Serial number: \"%s\"\n"
|
||||
msgstr "Erialsay umbernay: \"%s\"\n"
|
||||
|
||||
#: evtest/ev_test.c:117
|
||||
#: evtest/ev_test.c:118
|
||||
msgid "Testing (interrupt to exit)\n"
|
||||
msgstr "Estingtay (interruptay otay exitay)\n"
|
||||
|
||||
#: evtest/ev_test.c:157 evtest/ev_test.c:165
|
||||
#: evtest/ev_test.c:158 evtest/ev_test.c:166
|
||||
#, c-format
|
||||
msgid "Event @ %ld.%06ld: %s, value %d\n"
|
||||
msgstr "Eventay @ %ld.%06ld: %s, aluevay %d\n"
|
||||
|
|
@ -536,17 +536,17 @@ msgstr "Estingtay aracterchay 0x%02x..."
|
|||
msgid "OK"
|
||||
msgstr "OKay"
|
||||
|
||||
#: daemon/x52d_main.c:64
|
||||
#: daemon/x52d_main.c:67
|
||||
#, c-format
|
||||
msgid "Error %d setting log file: %s\n"
|
||||
msgstr "Erroray %d ettingsay oglay ilefay: %s\n"
|
||||
|
||||
#: daemon/x52d_main.c:80
|
||||
#: daemon/x52d_main.c:83
|
||||
#, c-format
|
||||
msgid "Error %d installing handler for signal %d: %s"
|
||||
msgstr "Erroray %d installingay andlerhay orfay ignalsay %d: %s"
|
||||
|
||||
#: daemon/x52d_main.c:91
|
||||
#: daemon/x52d_main.c:94
|
||||
#, c-format
|
||||
msgid ""
|
||||
"Usage: %s [-f] [-v] [-q]\n"
|
||||
|
|
@ -562,112 +562,112 @@ msgstr ""
|
|||
"\t[-b otifynay-ocketsay-athpay]\n"
|
||||
"\n"
|
||||
|
||||
#: daemon/x52d_main.c:124
|
||||
#: daemon/x52d_main.c:129
|
||||
#, c-format
|
||||
msgid "Daemon is already running as PID %u"
|
||||
msgstr "Aemonday isay alreadyay unningray asay IDPay %u"
|
||||
|
||||
#: daemon/x52d_main.c:266
|
||||
#: daemon/x52d_main.c:271
|
||||
#, c-format
|
||||
msgid "Unable to parse configuration override '%s'\n"
|
||||
msgstr "Unableay otay arsepay onfigurationcay overrideay '%s'\n"
|
||||
|
||||
#: daemon/x52d_main.c:298
|
||||
#: daemon/x52d_main.c:303
|
||||
#, c-format
|
||||
msgid "Foreground = %s"
|
||||
msgstr "Oregroundfay = %s"
|
||||
|
||||
#: daemon/x52d_main.c:298 daemon/x52d_main.c:299
|
||||
#: daemon/x52d_main.c:303 daemon/x52d_main.c:304
|
||||
msgid "true"
|
||||
msgstr "uetray"
|
||||
|
||||
#: daemon/x52d_main.c:298 daemon/x52d_main.c:299
|
||||
#: daemon/x52d_main.c:303 daemon/x52d_main.c:304
|
||||
msgid "false"
|
||||
msgstr "alsefay"
|
||||
|
||||
#: daemon/x52d_main.c:299
|
||||
#: daemon/x52d_main.c:304
|
||||
#, c-format
|
||||
msgid "Quiet = %s"
|
||||
msgstr "Uietqay = %s"
|
||||
|
||||
#: daemon/x52d_main.c:300
|
||||
#: daemon/x52d_main.c:305
|
||||
#, c-format
|
||||
msgid "Verbosity = %d"
|
||||
msgstr "Erbosityvay = %d"
|
||||
|
||||
#: daemon/x52d_main.c:301
|
||||
#: daemon/x52d_main.c:306
|
||||
#, c-format
|
||||
msgid "Log file = %s"
|
||||
msgstr "Oglay ilefay = %s"
|
||||
|
||||
#: daemon/x52d_main.c:302
|
||||
#: daemon/x52d_main.c:307
|
||||
#, c-format
|
||||
msgid "Config file = %s"
|
||||
msgstr "Onfigcay ilefay = %s"
|
||||
|
||||
#: daemon/x52d_main.c:303
|
||||
#: daemon/x52d_main.c:308
|
||||
#, c-format
|
||||
msgid "PID file = %s"
|
||||
msgstr "IDPay ilefay = %s"
|
||||
|
||||
#: daemon/x52d_main.c:304
|
||||
#: daemon/x52d_main.c:309
|
||||
#, c-format
|
||||
msgid "Command socket = %s"
|
||||
msgstr "Ommandcay ocketsay = %s"
|
||||
|
||||
#: daemon/x52d_main.c:305
|
||||
#: daemon/x52d_main.c:310
|
||||
#, c-format
|
||||
msgid "Notify socket = %s"
|
||||
msgstr "Otifynay ocketsay = %s"
|
||||
|
||||
#: daemon/x52d_main.c:316
|
||||
#: daemon/x52d_main.c:321
|
||||
#, c-format
|
||||
msgid "Error %d blocking signals on child threads: %s"
|
||||
msgstr "Erroray %d ockingblay ignalssay onay ildchay eadsthray: %s"
|
||||
|
||||
#: daemon/x52d_main.c:335
|
||||
#: daemon/x52d_main.c:340
|
||||
#, c-format
|
||||
msgid "Error %d unblocking signals on child threads: %s"
|
||||
msgstr "Erroray %d unblockingay ignalssay onay ildchay eadsthray: %s"
|
||||
|
||||
#: daemon/x52d_main.c:348
|
||||
#: daemon/x52d_main.c:353
|
||||
msgid "Reloading X52 configuration"
|
||||
msgstr "Eloadingray X52 onfigurationcay"
|
||||
|
||||
#: daemon/x52d_main.c:355
|
||||
#: daemon/x52d_main.c:360
|
||||
msgid "Saving X52 configuration to disk"
|
||||
msgstr "Avingsay X52 onfigurationcay otay iskday"
|
||||
|
||||
#: daemon/x52d_main.c:361
|
||||
#: daemon/x52d_main.c:366
|
||||
#, c-format
|
||||
msgid "Received termination signal %s"
|
||||
msgstr "Eceivedray erminationtay ignalsay %s"
|
||||
|
||||
#: daemon/x52d_main.c:378
|
||||
#: daemon/x52d_main.c:383
|
||||
msgid "Shutting down X52 daemon"
|
||||
msgstr "Uttingshay ownday X52 aemonday"
|
||||
|
||||
#: daemon/x52d_client.c:32
|
||||
#: daemon/x52d_client.c:37
|
||||
#, c-format
|
||||
msgid "Error accepting client connection on socket fd %d: %s"
|
||||
msgstr "Erroray acceptingay ientclay onnectioncay onay ocketsay fday %d: %s"
|
||||
|
||||
#: daemon/x52d_client.c:38
|
||||
#: daemon/x52d_client.c:43
|
||||
#, c-format
|
||||
msgid "Error marking client fd %d as nonblocking: %s"
|
||||
msgstr "Erroray arkingmay ientclay fday %d asay onblockingnay: %s"
|
||||
|
||||
#: daemon/x52d_client.c:87
|
||||
#: daemon/x52d_client.c:97
|
||||
#, c-format
|
||||
msgid "Error when polling socket: FD %d, error %d, len %lu"
|
||||
msgstr "Erroray enwhay ollingpay ocketsay: FDay %d, erroray %d, enlay %lu"
|
||||
|
||||
#: daemon/x52d_client.c:118
|
||||
#: daemon/x52d_client.c:127
|
||||
#, c-format
|
||||
msgid "Error %d when polling %d descriptors: %s"
|
||||
msgstr "Erroray %d enwhay ollingpay %d escriptorsday: %s"
|
||||
|
||||
#: daemon/x52d_client.c:121
|
||||
#: daemon/x52d_client.c:130
|
||||
msgid "Timed out when polling"
|
||||
msgstr "Imedtay outay enwhay ollingpay"
|
||||
|
||||
|
|
@ -676,7 +676,7 @@ msgstr "Imedtay outay enwhay ollingpay"
|
|||
msgid "Setting clock enable to %s"
|
||||
msgstr "Ettingsay ockclay enableay otay %s"
|
||||
|
||||
#: daemon/x52d_clock.c:35 daemon/x52d_clock.c:116
|
||||
#: daemon/x52d_clock.c:35 daemon/x52d_clock.c:119
|
||||
#, c-format
|
||||
msgid "Setting %s clock timezone to %s"
|
||||
msgstr "Ettingsay %s ockclay imezonetay otay %s"
|
||||
|
|
@ -700,80 +700,70 @@ msgid "Unable to backup timezone environment. Falling back to UTC"
|
|||
msgstr ""
|
||||
"Unableay otay ackupbay imezonetay environmentay. Allingfay ackbay otay UTCay"
|
||||
|
||||
#: daemon/x52d_clock.c:135
|
||||
#: daemon/x52d_clock.c:138
|
||||
#, c-format
|
||||
msgid "Setting %s clock format to %s"
|
||||
msgstr "Ettingsay %s ockclay ormatfay otay %s"
|
||||
|
||||
#: daemon/x52d_clock.c:159
|
||||
#: daemon/x52d_clock.c:162
|
||||
#, c-format
|
||||
msgid "Setting date format to %s"
|
||||
msgstr "Ettingsay ateday ormatfay otay %s"
|
||||
|
||||
#: daemon/x52d_clock.c:170
|
||||
#: daemon/x52d_clock.c:174
|
||||
msgid "Starting X52 clock manager thread"
|
||||
msgstr "Artingstay X52 ockclay anagermay eadthray"
|
||||
|
||||
#: daemon/x52d_clock.c:181
|
||||
#: daemon/x52d_clock.c:185
|
||||
#, c-format
|
||||
msgid "Error %d retrieving current time: %s"
|
||||
msgstr "Erroray %d etrievingray urrentcay imetay: %s"
|
||||
|
||||
#: daemon/x52d_clock.c:202
|
||||
#: daemon/x52d_clock.c:206
|
||||
#, c-format
|
||||
msgid "Error %d initializing clock thread: %s"
|
||||
msgstr "Erroray %d initializingay ockclay eadthray: %s"
|
||||
|
||||
#: daemon/x52d_clock.c:209
|
||||
#: daemon/x52d_clock.c:213
|
||||
msgid "Shutting down X52 clock manager thread"
|
||||
msgstr "Uttingshay ownday X52 ockclay anagermay eadthray"
|
||||
|
||||
#: daemon/x52d_command.c:367
|
||||
#: daemon/x52d_command.c:379
|
||||
#, c-format
|
||||
msgid "Error reading from client %d: %s"
|
||||
msgstr "Erroray eadingray omfray ientclay %d: %s"
|
||||
|
||||
#: daemon/x52d_command.c:378
|
||||
#: daemon/x52d_command.c:390
|
||||
#, c-format
|
||||
msgid "Short write to client %d; expected %d bytes, wrote %d bytes"
|
||||
msgstr ""
|
||||
"Ortshay itewray otay ientclay %d; expecteday %d ytesbay, otewray %d ytesbay"
|
||||
|
||||
#: daemon/x52d_command.c:402
|
||||
#: daemon/x52d_command.c:415
|
||||
#, c-format
|
||||
msgid "Error %d during command loop: %s"
|
||||
msgstr "Erroray %d uringday ommandcay ooplay: %s"
|
||||
|
||||
#: daemon/x52d_command.c:430
|
||||
#: daemon/x52d_command.c:442
|
||||
#, c-format
|
||||
msgid "Error creating command socket: %s"
|
||||
msgstr "Erroray eatingcray ommandcay ocketsay: %s"
|
||||
|
||||
#: daemon/x52d_command.c:437
|
||||
#: daemon/x52d_command.c:450
|
||||
#, c-format
|
||||
msgid "Error getting command socket flags: %s"
|
||||
msgstr "Erroray ettinggay ommandcay ocketsay agsflay: %s"
|
||||
msgid "Error marking command socket as nonblocking: %s"
|
||||
msgstr "Erroray arkingmay ommandcay ocketsay asay onblockingnay: %s"
|
||||
|
||||
#: daemon/x52d_command.c:441
|
||||
#, c-format
|
||||
msgid "Error setting command socket flags: %s"
|
||||
msgstr "Erroray ettingsay ommandcay ocketsay agsflay: %s"
|
||||
|
||||
#: daemon/x52d_command.c:449
|
||||
#, c-format
|
||||
msgid "Error binding to command socket: %s"
|
||||
msgstr "Erroray indingbay otay ommandcay ocketsay: %s"
|
||||
|
||||
#: daemon/x52d_command.c:454
|
||||
#: daemon/x52d_command.c:456
|
||||
#, c-format
|
||||
msgid "Error listening on command socket: %s"
|
||||
msgstr "Erroray isteninglay onay ommandcay ocketsay: %s"
|
||||
|
||||
#: daemon/x52d_command.c:464
|
||||
#: daemon/x52d_command.c:460
|
||||
msgid "Starting command processing thread"
|
||||
msgstr "Artingstay ommandcay ocessingpray eadthray"
|
||||
|
||||
#: daemon/x52d_command.c:482
|
||||
#: daemon/x52d_command.c:478
|
||||
msgid "Shutting down command processing thread"
|
||||
msgstr "Uttingshay ownday ommandcay ocessingpray eadthray"
|
||||
|
||||
|
|
@ -835,67 +825,67 @@ msgstr "Onay aluevay oundfay inay overrideay ingstray '%s'"
|
|||
msgid "Error processing override '%s.%s=%s'"
|
||||
msgstr "Erroray ocessingpray overriday '%s.%s=%s'"
|
||||
|
||||
#: daemon/x52d_device.c:36
|
||||
#: daemon/x52d_device.c:37
|
||||
msgid "Starting X52 device manager thread"
|
||||
msgstr "Artingstay X52 eviceday anagermay eadthray"
|
||||
|
||||
#: daemon/x52d_device.c:43
|
||||
#: daemon/x52d_device.c:44
|
||||
#, c-format
|
||||
msgid "Error %d connecting to device: %s"
|
||||
msgstr "Erroray %d onnectingcay otay eviceday: %s"
|
||||
|
||||
#: daemon/x52d_device.c:52
|
||||
#: daemon/x52d_device.c:53
|
||||
msgid "Device connected, writing configuration"
|
||||
msgstr "Eviceday onnectedcay, itingwray onfigurationcay"
|
||||
|
||||
#: daemon/x52d_device.c:75
|
||||
#: daemon/x52d_device.c:76
|
||||
msgid "Initializing libx52"
|
||||
msgstr "Initializingay libx52"
|
||||
|
||||
#: daemon/x52d_device.c:79
|
||||
#: daemon/x52d_device.c:80
|
||||
#, c-format
|
||||
msgid "Failure %d initializing libx52: %s"
|
||||
msgstr "Ailurefay %d initializeay libx52: %s"
|
||||
|
||||
#: daemon/x52d_device.c:90
|
||||
#: daemon/x52d_device.c:91
|
||||
msgid "Shutting down X52 device manager thread"
|
||||
msgstr "Uttingshay ownday X52 eviceday anagermay eadthray"
|
||||
|
||||
#: daemon/x52d_device.c:103
|
||||
#: daemon/x52d_device.c:104
|
||||
#, c-format
|
||||
msgid "Error %d when updating X52 parameter: %s"
|
||||
msgstr "Erroray %d enwhay updatingay X52 arameterpay: %s"
|
||||
|
||||
#: daemon/x52d_device.c:179
|
||||
#: daemon/x52d_device.c:180
|
||||
#, c-format
|
||||
msgid "Error %d when updating X52 device: %s"
|
||||
msgstr "Erroray %d enwhay updatingay X52 eviceday: %s"
|
||||
|
||||
#: daemon/x52d_io.c:42
|
||||
#: daemon/x52d_io.c:43
|
||||
msgid "Starting X52 I/O thread"
|
||||
msgstr "Artingstay X52 I/O eadthray"
|
||||
|
||||
#: daemon/x52d_io.c:64
|
||||
#: daemon/x52d_io.c:65
|
||||
#, c-format
|
||||
msgid "Error %d opening X52 I/O device: %s"
|
||||
msgstr "Erroray %d openingay X52 I/O eviceday: %s"
|
||||
|
||||
#: daemon/x52d_io.c:75
|
||||
#: daemon/x52d_io.c:76
|
||||
#, c-format
|
||||
msgid "Error %d reading from X52 I/O device: %s"
|
||||
msgstr "Erroray %d eadingray omfray X52 I/O eviceday: %s"
|
||||
|
||||
#: daemon/x52d_io.c:102
|
||||
#: daemon/x52d_io.c:103
|
||||
#, c-format
|
||||
msgid "Error %d initializing X52 I/O library: %s"
|
||||
msgstr "Erroray %d initializingay X52 ibrarylay: %s"
|
||||
|
||||
#: daemon/x52d_io.c:108
|
||||
#: daemon/x52d_io.c:109
|
||||
#, c-format
|
||||
msgid "Error %d initializing I/O driver thread: %s"
|
||||
msgstr "Erroray %d initializingay I/O iverdray eadthray: %s"
|
||||
|
||||
#: daemon/x52d_io.c:115
|
||||
#: daemon/x52d_io.c:116
|
||||
msgid "Shutting down X52 I/O driver thread"
|
||||
msgstr "Uttingshay ownday X52 I/O iverdray eadthray"
|
||||
|
||||
|
|
@ -938,24 +928,24 @@ msgstr "Erroray itingwray ousemay axisay eventay (axisay %d, aluevay %d)"
|
|||
msgid "Error writing mouse sync event"
|
||||
msgstr "Erroray itingwray ousemay yncsay eventay"
|
||||
|
||||
#: daemon/x52d_mouse_evdev.c:134
|
||||
#: daemon/x52d_mouse_evdev.c:135
|
||||
msgid "Starting X52 virtual mouse driver thread"
|
||||
msgstr "Artingstay X52 irtualvay ousemay iverdray eadthray"
|
||||
|
||||
#: daemon/x52d_mouse_evdev.c:157
|
||||
#: daemon/x52d_mouse_evdev.c:158
|
||||
#, c-format
|
||||
msgid "Error %d initializing mouse thread: %s"
|
||||
msgstr "Erroray %d initializingay ousemay eadthray: %s"
|
||||
|
||||
#: daemon/x52d_mouse_evdev.c:164
|
||||
#: daemon/x52d_mouse_evdev.c:165
|
||||
msgid "Shutting down X52 virtual mouse driver thread"
|
||||
msgstr "Uttingshay ownday X52 irtualvay ousemay iverdray eadthray"
|
||||
|
||||
#: daemon/x52d_mouse_evdev.c:171
|
||||
#: daemon/x52d_mouse_evdev.c:172
|
||||
msgid "Virtual mouse not created. Ignoring thread state change"
|
||||
msgstr "Irtualvay ousemay otnay eatedcray. Ignoringa eadthray atestay angechay"
|
||||
|
||||
#: daemon/x52d_mouse_evdev.c:236
|
||||
#: daemon/x52d_mouse_evdev.c:237
|
||||
#, c-format
|
||||
msgid "Error %d creating X52 virtual mouse: %s"
|
||||
msgstr "Erroray %d eatingcray X52 irtualvay ousemay: %s"
|
||||
|
|
@ -970,41 +960,36 @@ msgstr "Erroray eatingcray otificationnay ocketsay: %s"
|
|||
msgid "Error marking notification socket as nonblocking: %s"
|
||||
msgstr "Erroray arkingmay otificationnay ocketsay asay onblockingnay: %s"
|
||||
|
||||
#: daemon/x52d_notify.c:59
|
||||
#, c-format
|
||||
msgid "Error binding to notification socket: %s"
|
||||
msgstr "Erroray indingbay otay otificationnay ocketsay: %s"
|
||||
|
||||
#: daemon/x52d_notify.c:64
|
||||
#: daemon/x52d_notify.c:56
|
||||
#, c-format
|
||||
msgid "Error listening on notification socket: %s"
|
||||
msgstr "Erroray isteninglay onay otificationnay ocketsay: %s"
|
||||
|
||||
#: daemon/x52d_notify.c:73
|
||||
#: daemon/x52d_notify.c:65
|
||||
msgid "Error setting up notification socket"
|
||||
msgstr "Erroray ettingsay upay otificationnay ocketsay: %s"
|
||||
|
||||
#: daemon/x52d_notify.c:90 daemon/x52d_notify.c:106
|
||||
#: daemon/x52d_notify.c:81 daemon/x52d_notify.c:91
|
||||
#, c-format
|
||||
msgid "Error %d reading from pipe: %s"
|
||||
msgstr "Erroray eadingray omfray ipepay %d: %s"
|
||||
|
||||
#: daemon/x52d_notify.c:148
|
||||
#: daemon/x52d_notify.c:128
|
||||
#, c-format
|
||||
msgid "Error %d writing notification pipe: %s"
|
||||
msgstr "Erroray %d itingwray otificationnay ipepay: %s"
|
||||
|
||||
#: daemon/x52d_notify.c:193
|
||||
#: daemon/x52d_notify.c:174
|
||||
#, c-format
|
||||
msgid "Error %d creating notification pipe: %s"
|
||||
msgstr "Erroray %d eatingcray otificationnay ipepay: %s"
|
||||
|
||||
#: daemon/x52d_notify.c:202
|
||||
#: daemon/x52d_notify.c:183
|
||||
#, c-format
|
||||
msgid "Error %d initializing notify thread: %s"
|
||||
msgstr "Erroray %d initializingay otifynay eadthray: %s"
|
||||
|
||||
#: daemon/x52d_notify.c:208
|
||||
#: daemon/x52d_notify.c:189
|
||||
#, c-format
|
||||
msgid "Error %d initializing notify listener: %s"
|
||||
msgstr "Erroray %d initializingay otifynay istenerlay: %s"
|
||||
|
|
@ -1019,7 +1004,7 @@ msgstr "Usageay: %s [-i] [-s ocketsay-athpay] [ommandcay]\n"
|
|||
msgid "Argument length too long\n"
|
||||
msgstr "Argumentay engthlay ootay onglay\n"
|
||||
|
||||
#: daemon/x52ctl.c:142
|
||||
#: daemon/x52ctl.c:180
|
||||
#, c-format
|
||||
msgid "Running in interactive mode, ignoring extra arguments\n"
|
||||
msgstr "Unningray inay interactiveay odemay, ignoringay extraay argumentsay\n"
|
||||
|
|
|
|||
|
|
@ -4,5 +4,5 @@
|
|||
#
|
||||
# SPDX-License-Identifier: GPL-2.0-only WITH Classpath-exception-2.0
|
||||
|
||||
SUBDIRS = pinelog inih
|
||||
SUBDIRS = pinelog
|
||||
|
||||
|
|
@ -41,7 +41,7 @@ static void print_time_difference(const char *type, struct timespec *ts)
|
|||
type, tp_usec, tp_nsec, ret.tv_sec, ret.tv_nsec);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
int main(void)
|
||||
{
|
||||
struct timespec ts_wall[2];
|
||||
struct timespec ts_cpu[2];
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue