mirror of https://github.com/nirenjan/libx52.git
Compare commits
30 Commits
| Author | SHA1 | Date |
|---|---|---|
|
|
230951a232 | |
|
|
c99f775b70 | |
|
|
8914184613 | |
|
|
fdafda1d34 | |
|
|
9501813c36 | |
|
|
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 |
|
|
@ -2,10 +2,20 @@
|
|||
# Run the build and tests
|
||||
set -e
|
||||
|
||||
meson setup -Dprefix=/usr -Dsysconfdir=/etc -Dlocalstatedir=/var -Dnls=enabled build
|
||||
cd build
|
||||
ninja
|
||||
ninja test
|
||||
BUILDDIR="${1:-build}"
|
||||
|
||||
rm -rf "$BUILDDIR"
|
||||
|
||||
# Handle the meson dist failure in CI
|
||||
if [[ "$GITHUB_ACTIONS" == "true" ]]
|
||||
then
|
||||
git config --global --add safe.directory '*'
|
||||
fi
|
||||
|
||||
meson setup -Dprefix=/usr -Dsysconfdir=/etc -Dlocalstatedir=/var -Dnls=enabled "$BUILDDIR"
|
||||
cd "$BUILDDIR"
|
||||
meson compile
|
||||
meson test
|
||||
meson dist
|
||||
|
||||
# Print bugreport output
|
||||
|
|
|
|||
|
|
@ -0,0 +1,63 @@
|
|||
#!/usr/bin/env python3
|
||||
"""Generate the list of distros as a build matrix"""
|
||||
|
||||
import pathlib
|
||||
import json
|
||||
import sys
|
||||
|
||||
class BuildMatrix:
|
||||
"""Generate a build matrix for Github actions"""
|
||||
|
||||
COMPILER = 'gcc'
|
||||
OS = 'ubuntu-latest'
|
||||
EXPERIMENTAL = False
|
||||
|
||||
def __init__(self, image_prefix):
|
||||
self.matrix = []
|
||||
self.image_prefix = image_prefix
|
||||
self.get_distros()
|
||||
self.add_extra_builds()
|
||||
self.generate_output()
|
||||
|
||||
def build_matrix_obj(self, distro, experimental, os=None, compiler=None, image=None):
|
||||
"""Build the matrix object for the given distro"""
|
||||
matrix_obj = {
|
||||
'distro': distro,
|
||||
'experimental': experimental,
|
||||
'os': os or self.OS,
|
||||
'compiler': compiler or self.COMPILER,
|
||||
}
|
||||
|
||||
if image is None:
|
||||
image = f"{self.image_prefix}/ci-build-{distro}:latest"
|
||||
|
||||
matrix_obj['image'] = image
|
||||
|
||||
return matrix_obj
|
||||
|
||||
def get_distros(self):
|
||||
"""Get the list of distros from the Dockerfiles"""
|
||||
for dockerfile in pathlib.Path('.').glob('docker/Dockerfile.*'):
|
||||
distro = dockerfile.suffix[1:]
|
||||
|
||||
with open(dockerfile, encoding='utf-8') as dfd:
|
||||
experimental = 'experimental="true"' in dfd.read()
|
||||
|
||||
self.matrix.append(self.build_matrix_obj(distro, experimental))
|
||||
|
||||
def add_extra_builds(self):
|
||||
"""Add manual canary builds that don't have a corresponding dockerfile"""
|
||||
canary_build = self.build_matrix_obj('ubuntu22', False, compiler='clang')
|
||||
self.matrix.append(canary_build)
|
||||
|
||||
macos_build = self.build_matrix_obj('macos', True, os='macos-latest',
|
||||
compiler='clang', image='')
|
||||
self.matrix.append(macos_build)
|
||||
|
||||
def generate_output(self):
|
||||
"""Generate the output for github actions"""
|
||||
matrix_data = json.dumps(self.matrix)
|
||||
print(f"matrix={matrix_data}")
|
||||
|
||||
if __name__ == '__main__':
|
||||
BuildMatrix(sys.argv[1])
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
#!/bin/bash
|
||||
# Get the list of changed Dockerfiles
|
||||
# Usage: get-changed-dockerfiles.sh <before-SHA> <head-SHA>
|
||||
|
||||
set -euxo pipefail
|
||||
|
||||
mapfile -t ALL_DOCKERFILES < <(git ls-files 'docker/Dockerfile.*')
|
||||
if [[ -n "${BUILD_DOCKER_MANUAL_ENV:-}" ]]
|
||||
then
|
||||
|
||||
if [[ "${BUILD_DOCKER_MANUAL_ENV}" == "ALL" ]]
|
||||
then
|
||||
DOCKERFILES=("${ALL_DOCKERFILES[@]}")
|
||||
else
|
||||
BUILD_LIST=($BUILD_DOCKER_MANUAL_ENV)
|
||||
DOCKERFILES=()
|
||||
for item in "${BUILD_LIST[@]}"
|
||||
do
|
||||
if [[ -f "docker/Dockerfile.${item}" ]]
|
||||
then
|
||||
DOCKERFILES+=("$item")
|
||||
fi
|
||||
done
|
||||
fi
|
||||
else
|
||||
CHANGED_FILES=$(git diff --name-only --diff-filter=ACMR HEAD^..HEAD)
|
||||
|
||||
mapfile -t DOCKERFILES < <(echo "$CHANGED_FILES" | grep 'docker/Dockerfile')
|
||||
mapfile -t SCRIPT_CHANGES < <(echo "$CHANGED_FILES" | grep 'docker/' | grep '\.sh$')
|
||||
|
||||
for file in "${SCRIPT_CHANGES[@]}"
|
||||
do
|
||||
for dockerfile in "${ALL_DOCKERFILES[@]}"
|
||||
do
|
||||
if grep -q "$(basename "$file")" "$dockerfile"
|
||||
then
|
||||
DOCKERFILES+=("$dockerfile")
|
||||
fi
|
||||
done
|
||||
done
|
||||
fi
|
||||
|
||||
echo -n "matrix="
|
||||
echo "${DOCKERFILES[@]}" | \
|
||||
tr ' ' '\n' | sort -u | sed 's,docker/Dockerfile\.,,' | \
|
||||
jq -Rsc 'split("\n") | map(select(length > 0)) | unique'
|
||||
|
|
@ -10,34 +10,46 @@ on:
|
|||
- '!gh-pages'
|
||||
paths-ignore:
|
||||
- 'kernel_module/**'
|
||||
- 'docker/**'
|
||||
pull_request:
|
||||
branches: [ master ]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
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-') }}
|
||||
env:
|
||||
CC: ${{ matrix.cc }}
|
||||
list-distros:
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
matrix: ${{ steps.set-matrix.outputs.matrix }}
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- id: set-matrix
|
||||
run: .github/scripts/generate_build_matrix.py ghcr.io/${{ github.repository }} >> $GITHUB_OUTPUT
|
||||
|
||||
build:
|
||||
needs: list-distros
|
||||
if: "!(contains(github.event.head_commit.message, '[ci skip]') || contains(github.event.head_commit.message, '[skip ci]'))"
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: ['ubuntu-22.04', 'ubuntu-24.04', 'macos-latest']
|
||||
cc: ['gcc', 'clang']
|
||||
include: ${{ fromJson(needs.list-distros.outputs.matrix) }}
|
||||
|
||||
name: ${{ format('{0}/{1}{2}', matrix.distro, matrix.compiler, matrix.experimental == true && ' (experimental)' || '') }}
|
||||
runs-on: ${{ matrix.os }}
|
||||
continue-on-error: ${{ matrix.experimental }}
|
||||
container:
|
||||
image: ${{ matrix.image }}
|
||||
credentials:
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Install dependencies (Ubuntu)
|
||||
run: ./.github/scripts/install-dependencies-ubuntu.sh
|
||||
if: ${{ startsWith(matrix.os, 'ubuntu-') }}
|
||||
|
||||
- name: Install dependencies (MacOS)
|
||||
run: ./.github/scripts/install-dependencies-macos.sh
|
||||
if: ${{ startsWith(matrix.os, 'macos-') }}
|
||||
|
||||
- name: Build and Test
|
||||
env:
|
||||
CC: ${{ matrix.compiler }}
|
||||
run: ./.github/scripts/build-and-test.sh
|
||||
|
|
|
|||
|
|
@ -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@v6
|
||||
with:
|
||||
files: buildd/meson-logs/coverage.xml
|
||||
fail_ci_if_error: true
|
||||
|
|
@ -0,0 +1,66 @@
|
|||
name: Build Docker CI Images
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
distros:
|
||||
description: "List distros to build (space separated)"
|
||||
type: string
|
||||
default: "ALL"
|
||||
push:
|
||||
paths:
|
||||
- "docker/Dockerfile.*"
|
||||
- "docker/*.sh"
|
||||
|
||||
jobs:
|
||||
detect-changes:
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
BUILD_DOCKER_MANUAL_ENV: ${{ inputs.distros || '' }}
|
||||
outputs:
|
||||
matrix: ${{ steps.set-matrix.outputs.matrix }}
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
fetch-depth: 0 # Needed for detecting changes
|
||||
|
||||
- name: Set build matrix
|
||||
id: set-matrix
|
||||
run: .github/scripts/get-changed-dockerfiles.sh >> $GITHUB_OUTPUT
|
||||
|
||||
build-and-push:
|
||||
needs: detect-changes
|
||||
if: ${{ needs.detect-changes.outputs.matrix != '[]' }}
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
fail-fast: false # Don't cancel other distros if this one fails
|
||||
matrix:
|
||||
distro: ${{ fromJson(needs.detect-changes.outputs.matrix) }}
|
||||
permissions:
|
||||
packages: write
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Login to GHCR
|
||||
uses: docker/login-action@v4
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Build and push
|
||||
uses: docker/build-push-action@v7
|
||||
with:
|
||||
context: docker
|
||||
file: ./docker/Dockerfile.${{ matrix.distro }}
|
||||
push: true
|
||||
tags: ghcr.io/${{ github.repository }}/ci-build-${{ matrix.distro }}:latest
|
||||
|
||||
- name: Cleanup old builds
|
||||
uses: actions/delete-package-versions@v5
|
||||
with:
|
||||
package-name: libx52/ci-build-${{ matrix.distro }}
|
||||
package-type: container
|
||||
delete-only-untagged-versions: true
|
||||
|
|
@ -38,4 +38,4 @@ jobs:
|
|||
|
||||
- name: Deploy to GitHub Pages
|
||||
id: deployment
|
||||
uses: actions/deploy-pages@v4
|
||||
uses: actions/deploy-pages@v5
|
||||
|
|
|
|||
15
ChangeLog.md
15
ChangeLog.md
|
|
@ -6,8 +6,23 @@ The format is based upon [Keep a Changelog].
|
|||
|
||||
## [Unreleased]
|
||||
|
||||
### Changed
|
||||
|
||||
- Migrated CI builds to run in multiple distro containers.
|
||||
|
||||
### 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.
|
||||
|
|
|
|||
842
Doxyfile.in
842
Doxyfile.in
File diff suppressed because it is too large
Load Diff
50
INSTALL.md
50
INSTALL.md
|
|
@ -5,22 +5,22 @@ Build has been tested on the following operating systems (x86-64 only):
|
|||
|
||||
* Ubuntu 22.04 LTS
|
||||
* Ubuntu 24.04 LTS
|
||||
* macOS Big Sur 11
|
||||
* macOS Monterey 12
|
||||
* Fedora latest (Fedora 42, as of this commit)
|
||||
* Archlinux
|
||||
* Alpine Linux (Experimental)
|
||||
* Ubuntu 26.04 LTS (Experimental as of this commit)
|
||||
* macOS (latest tag on Github, ARM)
|
||||
|
||||
# Prerequisites
|
||||
|
||||
## Required Packages
|
||||
|
||||
* automake
|
||||
* autoconf
|
||||
* autopoint
|
||||
* meson
|
||||
* ninja
|
||||
* gettext
|
||||
* hidapi + headers
|
||||
* inih
|
||||
* libtool
|
||||
* 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)
|
||||
|
|
@ -29,15 +29,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 libinih-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 libinih python git` |
|
||||
| Fedora | `sudo dnf install autoconf automake gettext-devel findutils libtool hidapi-devel libusb-devel libevdev-devel inih-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
|
||||
|
||||
|
|
@ -49,37 +50,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
|
||||
|
|
@ -89,13 +89,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
|
||||
```
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -65,7 +65,7 @@ devinfo_cleanup:
|
|||
libx52io_exit(ctx);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
int main(void)
|
||||
{
|
||||
const struct libusb_version *libusb;
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
# x52d
|
||||
libx52dcomm_version = '1.0.0'
|
||||
|
||||
libx52dcomm_sources = [
|
||||
'x52d_comm_client.c',
|
||||
'x52d_comm_internal.c'
|
||||
|
|
@ -8,6 +10,8 @@ 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 = [
|
||||
|
|
@ -43,7 +47,7 @@ exe_x52d = executable('x52d', x52d_sources,
|
|||
dependencies: x52d_deps,
|
||||
link_with: x52d_linkwith)
|
||||
|
||||
executable('x52ctl', 'x52ctl.c',
|
||||
exe_x52ctl = executable('x52ctl', 'x52ctl.c',
|
||||
install: true,
|
||||
dependencies: [dep_intl],
|
||||
include_directories: includes,
|
||||
|
|
@ -53,7 +57,7 @@ install_data('x52d.conf',
|
|||
install_dir: join_paths(get_option('sysconfdir'), 'x52d'))
|
||||
|
||||
test('daemon-communication', files('test_daemon_comm.py')[0],
|
||||
depends: exe_x52d, protocol: 'tap')
|
||||
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,
|
||||
|
|
@ -62,3 +66,24 @@ x52d_mouse_test = executable('x52d-mouse-test', x52d_mouse_test_sources,
|
|||
|
||||
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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -169,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 (;;) {
|
||||
|
|
|
|||
|
|
@ -409,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"),
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -71,6 +71,7 @@ static void * x52_notify_thr(void * param)
|
|||
char buffer[X52D_BUFSZ];
|
||||
uint16_t bufsiz;
|
||||
int rc;
|
||||
(void)param;
|
||||
|
||||
for (;;) {
|
||||
do {
|
||||
|
|
@ -146,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);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,8 @@
|
|||
FROM archlinux:base-devel
|
||||
|
||||
LABEL org.opencontainers.image.description="INTERNAL CI USE ONLY - Not intended for production"
|
||||
LABEL org.opencontainers.image.authors="Nirenjan Krishnan"
|
||||
|
||||
COPY ./install-dependencies-archlinux.sh ./ci-setup.sh /tmp/
|
||||
|
||||
RUN /tmp/install-dependencies-archlinux.sh && /tmp/ci-setup.sh
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
FROM fedora:latest
|
||||
|
||||
LABEL org.opencontainers.image.description="INTERNAL CI USE ONLY - Not intended for production"
|
||||
LABEL org.opencontainers.image.authors="Nirenjan Krishnan"
|
||||
|
||||
COPY ./install-dependencies-fedora.sh ./ci-setup.sh /tmp/
|
||||
|
||||
RUN /tmp/install-dependencies-fedora.sh && dnf clean all && /tmp/ci-setup.sh
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
FROM ubuntu:22.04
|
||||
|
||||
LABEL org.opencontainers.image.description="INTERNAL CI USE ONLY - Not intended for production"
|
||||
LABEL org.opencontainers.image.authors="Nirenjan Krishnan"
|
||||
|
||||
ENV DEBIAN_FRONTEND=noninteractive
|
||||
|
||||
COPY ./install-dependencies-ubuntu.sh ./ci-setup.sh /tmp/
|
||||
|
||||
RUN /tmp/install-dependencies-ubuntu.sh && \
|
||||
rm -rf /var/lib/apt/lists/* && \
|
||||
/tmp/ci-setup.sh
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
FROM ubuntu:24.04
|
||||
|
||||
LABEL org.opencontainers.image.description="INTERNAL CI USE ONLY - Not intended for production"
|
||||
LABEL org.opencontainers.image.authors="Nirenjan Krishnan"
|
||||
|
||||
ENV DEBIAN_FRONTEND=noninteractive
|
||||
|
||||
COPY ./install-dependencies-ubuntu.sh ./ci-setup.sh /tmp/
|
||||
|
||||
RUN /tmp/install-dependencies-ubuntu.sh && \
|
||||
rm -rf /var/lib/apt/lists/* && \
|
||||
/tmp/ci-setup.sh
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
FROM ubuntu:26.04
|
||||
|
||||
LABEL org.opencontainers.image.description="INTERNAL CI USE ONLY - Not intended for production"
|
||||
LABEL org.opencontainers.image.authors="Nirenjan Krishnan"
|
||||
LABEL com.project.ci.experimental="true"
|
||||
|
||||
ENV DEBIAN_FRONTEND=noninteractive
|
||||
|
||||
COPY ./install-dependencies-ubuntu.sh ./ci-setup.sh /tmp/
|
||||
|
||||
RUN /tmp/install-dependencies-ubuntu.sh && \
|
||||
rm -rf /var/lib/apt/lists/* && \
|
||||
/tmp/ci-setup.sh
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
Dockerfiles for CI builds
|
||||
=========================
|
||||
|
||||
This repo contains a number of Dockerfiles for building libx52 against multiple
|
||||
distros. Github only supports Ubuntu LTS for it's Linux runners, but libx52
|
||||
users run multiple distros, including Arch Linux, Gentoo and Fedora.
|
||||
|
||||
For this reason, it's better to get a prebuilt Docker image that builds the CI
|
||||
environment, pulling in all the relevant dependencies, and staging the CI build
|
||||
environment as a container within Github. The regular build pipeline can then
|
||||
run on these containers, and this will help ensure that the breakages are
|
||||
reduced to a minimum.
|
||||
|
||||
Build Instructions
|
||||
==================
|
||||
|
||||
In order to run the CI build on all the supported platforms, first run the
|
||||
`build-containers.sh` script, it will find the Dockerfiles for all the
|
||||
suppported platforms in the `docker` directory, and build the container images
|
||||
for them with the necessary dependencies included.
|
||||
|
||||
Once the container images are built, you can build against one or all of the
|
||||
container images by running `build-repo.sh`. If you don't specify the container
|
||||
distro, which is basically the same as the extension after `Dockerfile.`, it
|
||||
will run everything. If you specify a distro that doesn't exist, or has not been
|
||||
built, the script will silently exit.
|
||||
|
||||
Extending to a new distro
|
||||
=========================
|
||||
|
||||
To extend the builds to a new distro, create a `Dockerfile.<distro>` with the
|
||||
necessary instructions to build a container image for that distro. Make sure you
|
||||
install the necessary dependencies for the distro. It is strongly recommended to
|
||||
add a `install-dependencies-<distro>.sh` so that you can build against multiple
|
||||
versions of that distro, eg. Ubuntu 22.04 and Ubuntu 24.04. Make sure you copy
|
||||
`ci-setup.sh` to allow setting up the environment, since it is highly likely
|
||||
that the `meson dist` command will fail if you do not run that script.
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
#!/bin/bash
|
||||
# Build containers for all the target environments
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR=$(dirname "$0")
|
||||
if command -v realpath &>/dev/null
|
||||
then
|
||||
SCRIPT_DIR=$(realpath "$SCRIPT_DIR")
|
||||
fi
|
||||
|
||||
GITHUB_SCRIPTS_DIR="$SCRIPT_DIR/../.github/scripts"
|
||||
if command -v realpath &>/dev/null
|
||||
then
|
||||
GITHUB_SCRIPTS_DIR=$(realpath "$GITHUB_SCRIPTS_DIR")
|
||||
fi
|
||||
|
||||
for dockerfile in "$SCRIPT_DIR"/[Dd]ockerfile.*
|
||||
do
|
||||
if [[ -e "$dockerfile" ]]
|
||||
then
|
||||
SUFFIX="$(basename "$dockerfile" | cut -d. -f2)"
|
||||
TAG="ghcr.io/nirenjan/libx52/ci-build-${SUFFIX}:latest"
|
||||
docker build --tag "$TAG" -f "$dockerfile" "${SCRIPT_DIR}"
|
||||
fi
|
||||
done
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
#!/bin/bash
|
||||
# Build the repository for all found directories
|
||||
# Build containers for all the target environments
|
||||
set -euo pipefail
|
||||
|
||||
GIT_ROOT=$(git rev-parse --show-toplevel)
|
||||
CC=${CC:-gcc}
|
||||
|
||||
IMAGE="${1:-*}"
|
||||
|
||||
for image in $(docker images --filter "reference=ghcr.io/nirenjan/libx52/ci-build-${IMAGE}" --format '{{ .Repository}}')
|
||||
do
|
||||
distro=${image##*/ci-build-}
|
||||
container_name="libx52-runner-${distro}"
|
||||
|
||||
if [[ "$(docker ps -aq -f name=$container_name)" ]]
|
||||
then
|
||||
echo "Cleaning up old container for '$distro'"
|
||||
|
||||
docker rm -f $container_name >/dev/null
|
||||
fi
|
||||
|
||||
experimental=$(docker inspect --format='{{.Config.Labels}}' $image | \
|
||||
grep -q 'experimental:true' && echo " (experimental)" || true)
|
||||
|
||||
if docker run --rm --name $container_name \
|
||||
--device /dev/bus/usb:/dev/bus/usb \
|
||||
-v "$GIT_ROOT":/code \
|
||||
-w /code \
|
||||
-e CC="${CC}" \
|
||||
$image \
|
||||
/bin/bash -c ".github/scripts/build-and-test.sh builddir/${distro}" \
|
||||
&> "$GIT_ROOT/build-${distro}.log"
|
||||
then
|
||||
echo "=== ${distro}${experimental} OK ==="
|
||||
else
|
||||
echo "=== ${distro}${experimental} !!! FAIL !!! ==="
|
||||
tail -20 "$GIT_ROOT/build-${distro}.log"
|
||||
fi
|
||||
done
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
#!/bin/sh
|
||||
# Common CI setup
|
||||
|
||||
git config --global --add safe.directory /code
|
||||
git config --global --add safe.directory '*'
|
||||
|
||||
cat >> /root/.bashrc <<"EOF"
|
||||
echo 'WARNING: This is an internal CI container'
|
||||
echo 'Do not use for production'
|
||||
EOF
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
# This file is deliberately named with a lowercase d, in order to avoid the
|
||||
# Github action logic from picking this up as a supported distro. The Alpine
|
||||
# image fails in the Github actions, because it needs the /dev/bus/usb device
|
||||
# mounted inside the container, however, attempting to do so causes every
|
||||
# build to fail. Therefore, we disable the Alpine image in CI, but keep it
|
||||
# local, so that we can test the build against Alpine locally if necessary.
|
||||
FROM alpine:latest
|
||||
|
||||
LABEL org.opencontainers.image.description="INTERNAL CI USE ONLY - Not intended for production"
|
||||
LABEL org.opencontainers.image.authors="Nirenjan Krishnan"
|
||||
LABEL com.project.ci.experimental="true"
|
||||
|
||||
COPY ./install-dependencies-alpine.sh ./ci-setup.sh /tmp/
|
||||
RUN /tmp/install-dependencies-alpine.sh
|
||||
RUN /tmp/ci-setup.sh
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
#!/bin/sh
|
||||
# Setup for alpine
|
||||
set -eux
|
||||
|
||||
apk update
|
||||
apk add --no-cache \
|
||||
build-base \
|
||||
meson \
|
||||
bash \
|
||||
git \
|
||||
gettext \
|
||||
libusb-dev \
|
||||
hidapi-dev \
|
||||
libevdev-dev \
|
||||
inih-dev \
|
||||
cmocka-dev \
|
||||
tzdata \
|
||||
musl-libintl \
|
||||
doxygen
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
#!/bin/bash
|
||||
# Install dependencies on Archlinux container
|
||||
# Assumes that it's running off a base-devel tag
|
||||
set -euo pipefail
|
||||
|
||||
pacman -Syu --noconfirm \
|
||||
git \
|
||||
meson \
|
||||
libusb \
|
||||
hidapi \
|
||||
libinih \
|
||||
libevdev \
|
||||
python \
|
||||
gettext
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
#!/bin/bash
|
||||
# Install dependencies on Fedora container
|
||||
set -euo pipefail
|
||||
|
||||
dnf update -y
|
||||
dnf install -y \
|
||||
gcc \
|
||||
git \
|
||||
meson \
|
||||
libusb1-devel \
|
||||
hidapi-devel \
|
||||
inih-devel \
|
||||
libevdev-devel \
|
||||
pkg-config \
|
||||
python3 \
|
||||
gettext-devel
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
#!/bin/bash
|
||||
# Install dependencies to build and test on Ubuntu runners
|
||||
apt-get update && apt-get upgrade -y
|
||||
apt-get install -y \
|
||||
git \
|
||||
gcc clang \
|
||||
meson \
|
||||
gettext \
|
||||
pkg-config \
|
||||
python3 \
|
||||
libusb-1.0-0-dev \
|
||||
libhidapi-dev \
|
||||
libevdev-dev \
|
||||
libinih-dev \
|
||||
doxygen \
|
||||
libcmocka-dev \
|
||||
tzdata
|
||||
|
||||
exit 0
|
||||
|
|
@ -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,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
|
||||
|
|
@ -1,3 +1,5 @@
|
|||
libx52_version = '2.4.2'
|
||||
|
||||
libx52_files = files(
|
||||
'x52_control.c',
|
||||
'x52_core.c',
|
||||
|
|
@ -9,12 +11,16 @@ libx52_files = files(
|
|||
|
||||
lib_libx52 = library('x52', libx52_files,
|
||||
install: true,
|
||||
version: '2.4.2',
|
||||
version: libx52_version,
|
||||
dependencies: [dep_libusb, dep_intl],
|
||||
include_directories: [includes])
|
||||
|
||||
install_headers('libx52.h', subdir: meson.project_name())
|
||||
pkgconfig.generate(lib_libx52)
|
||||
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',
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@
|
|||
|
||||
#define TEST_STRINGIFY(name) do { \
|
||||
char expected[256]; \
|
||||
for (int i=-1; i < sizeof(name ## _map) / sizeof(name ## _map[0]); i++) { \
|
||||
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) { \
|
||||
|
|
@ -31,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",
|
||||
|
|
@ -52,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",
|
||||
|
|
@ -66,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",
|
||||
|
|
@ -78,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",
|
||||
|
|
@ -89,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",
|
||||
|
|
@ -103,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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
libx52io_version = '1.0.0'
|
||||
|
||||
libx52io_files = files(
|
||||
'io_core.c',
|
||||
'io_axis.c',
|
||||
|
|
@ -8,12 +10,17 @@ libx52io_files = files(
|
|||
|
||||
lib_libx52io = library('x52io', libx52io_files,
|
||||
install: true,
|
||||
version: '1.0.0',
|
||||
version: libx52io_version,
|
||||
dependencies: [dep_hidapi, dep_intl],
|
||||
include_directories: [includes])
|
||||
|
||||
install_headers('libx52io.h', subdir: meson.project_name())
|
||||
pkgconfig.generate(lib_libx52io)
|
||||
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,
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -20,33 +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
|
||||
char_map_TARGETS = \
|
||||
libx52util/util_char_map.c \
|
||||
libx52util/x52_char_map.bin
|
||||
|
||||
util_char_map_c_DEPENDS = \
|
||||
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)
|
||||
|
||||
if HAVE_CMOCKA
|
||||
TESTS += libx52util-map-test
|
||||
TESTS += libx52util/test-runner.sh
|
||||
|
||||
check_PROGRAMS += libx52util-map-test
|
||||
|
||||
CLEANFILES += libx52util/x52_map_test.c
|
||||
x52_map_test_c_DEPENDS = \
|
||||
$(srcdir)/libx52util/x52_map_test_gen.py \
|
||||
$(srcdir)/libx52util/x52_char_map.cfg
|
||||
|
||||
libx52util/x52_map_test.c: $(x52_map_test_c_DEPENDS)
|
||||
$(AM_V_GEN) $(PYTHON) $(x52_map_test_c_DEPENDS) $@
|
||||
|
||||
libx52util_map_test_SOURCES = libx52util/x52_map_test.c
|
||||
libx52util_map_test_CFLAGS = @CMOCKA_CFLAGS@ -I $(top_srcdir) -I $(top_srcdir)/libx52util
|
||||
libx52util_map_test_LDFLAGS = @CMOCKA_LIBS@
|
||||
libx52util_map_test_LDADD = libx52util.la
|
||||
endif
|
||||
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 \
|
||||
|
|
|
|||
|
|
@ -1,37 +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@', '@OUTPUT@'],
|
||||
command: [python, gen_script, '@INPUT@', '@OUTPUT0@', '@OUTPUT1@'],
|
||||
input: 'x52_char_map.cfg',
|
||||
output: 'util_char_map.c')
|
||||
output: ['util_char_map.c', 'x52_char_map.bin'])
|
||||
|
||||
lib_libx52util = library('x52util', util_char_map, 'x52_char_map_lookup.c',
|
||||
install: true,
|
||||
version: '1.0.1',
|
||||
version: libx52util_version,
|
||||
include_directories: [includes],
|
||||
)
|
||||
|
||||
install_headers('libx52util.h', subdir: meson.project_name())
|
||||
pkgconfig.generate(lib_libx52util)
|
||||
pkgconfig.generate(lib_libx52util,
|
||||
name: 'libx52util',
|
||||
description: 'Extra utility functions to manage X52 extended functionality',
|
||||
subdirs: meson.project_name(),
|
||||
version: libx52util_version,
|
||||
)
|
||||
|
||||
test_gen_script = files('x52_map_test_gen.py')[0]
|
||||
|
||||
libx52util_map_test_src = custom_target('libx52util-map-test-src',
|
||||
build_by_default: false,
|
||||
depend_files: ['x52_map_test_gen.py', 'x52_char_map.cfg'],
|
||||
command: [python, test_gen_script, '@INPUT@', '@OUTPUT@'],
|
||||
input: 'x52_char_map.cfg',
|
||||
output: 'x52_map_test.c'
|
||||
)
|
||||
|
||||
libx52util_map_test = executable('libx52util-map-test', libx52util_map_test_src,
|
||||
dependencies: [dep_cmocka],
|
||||
link_with: [lib_libx52util],
|
||||
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-map-test', libx52util_map_test, protocol: 'tap')
|
||||
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,52 +85,61 @@
|
|||
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 && *input) {
|
||||
} 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]);
|
||||
for (int i = 1; i <= seq_len; i++) {
|
||||
output[index] = sequence[i];
|
||||
index++;
|
||||
}
|
||||
} else {
|
||||
/* Invalid value, skip */
|
||||
while (*input >= 0x80 && *input < 0xC0) {
|
||||
input++; /* Skip invalid characters */
|
||||
}
|
||||
|
||||
/* New UTF-8 character, reset the entry pointer */
|
||||
entry = &map_root[*input];
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -1,107 +0,0 @@
|
|||
#!/usr/bin/env python3
|
||||
"""Generate a test script for the convert function"""
|
||||
|
||||
import argparse
|
||||
import re
|
||||
|
||||
def parse_file(map_file):
|
||||
"""Read the map file, strip out comments, and return a dictionary that
|
||||
maps the UTF-8 encoded string to the X52 MFD character"""
|
||||
|
||||
# If we are running this, then we know that the input map is likely
|
||||
# in a sane format already.
|
||||
char_dict = {}
|
||||
|
||||
with open(map_file, 'r', encoding='utf-8') as map_fd:
|
||||
for line in map_fd:
|
||||
line = re.sub(r'#.*$', '', line).strip()
|
||||
|
||||
if not line:
|
||||
# Comment line, skip
|
||||
continue
|
||||
|
||||
key, out = line.split()
|
||||
in_char = int(key, 0)
|
||||
|
||||
if len(out) == 1:
|
||||
out_byte = ord(out)
|
||||
else:
|
||||
out_byte = int(out, 0)
|
||||
|
||||
char_dict[in_char] = out_byte
|
||||
|
||||
return char_dict
|
||||
|
||||
def generate_positive_test_cases(char_dict):
|
||||
"""Generate a set of positive test cases"""
|
||||
# For every string in the dictionary, generate a test case that tests
|
||||
# the input against the output
|
||||
TEST_CASE_FMT = """
|
||||
static void test_map_{in_char}(void **state) {{
|
||||
(void)state;
|
||||
const uint8_t input_array[] = {{ {in_bytes}, 0 }};
|
||||
const uint8_t expected_output[2] = {{ {out_byte}, 0 }};
|
||||
size_t out_len = 20;
|
||||
uint8_t output[20] = {{ 0 }};
|
||||
int rc;
|
||||
|
||||
rc = libx52util_convert_utf8_string(input_array, output, &out_len);
|
||||
assert_int_equal(rc, 0);
|
||||
assert_int_equal(out_len, 1);
|
||||
assert_memory_equal(output, expected_output, 2);
|
||||
}}
|
||||
"""
|
||||
|
||||
output = ""
|
||||
for in_char, out_byte in char_dict.items():
|
||||
in_bytes = ", ".join(hex(c) for c in chr(in_char).encode('utf-8'))
|
||||
in_tc = hex(in_char)
|
||||
|
||||
output += TEST_CASE_FMT.format(in_char=in_tc, in_bytes=in_bytes, out_byte=out_byte)
|
||||
|
||||
output += """
|
||||
const struct CMUnitTest tests[] = {
|
||||
"""
|
||||
|
||||
for in_char in sorted(char_dict.keys()):
|
||||
output += f" cmocka_unit_test(test_map_{hex(in_char)}),\n"
|
||||
|
||||
output += '};\n'
|
||||
|
||||
return output
|
||||
|
||||
TEST_HEADER = """
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <stdarg.h>
|
||||
#include <setjmp.h>
|
||||
#include <cmocka.h>
|
||||
|
||||
#include "libx52util.h"
|
||||
"""
|
||||
|
||||
TEST_FOOTER = """
|
||||
int main(void) {
|
||||
cmocka_set_message_output(CM_OUTPUT_TAP);
|
||||
cmocka_run_group_tests(tests, NULL, NULL);
|
||||
return 0;
|
||||
}
|
||||
"""
|
||||
|
||||
def main():
|
||||
"""Generate X52 map test suite"""
|
||||
parser = argparse.ArgumentParser(description='Generate map test cases')
|
||||
parser.add_argument('INPUT_FILE', help="Input character map file")
|
||||
parser.add_argument('OUTPUT_FILE', help="Generated test script")
|
||||
args = parser.parse_args()
|
||||
|
||||
char_dict = parse_file(args.INPUT_FILE)
|
||||
test_cases = generate_positive_test_cases(char_dict)
|
||||
|
||||
with open(args.OUTPUT_FILE, 'w', encoding='utf-8') as out_fd:
|
||||
print(TEST_HEADER, file=out_fd)
|
||||
print(test_cases, file=out_fd)
|
||||
print(TEST_FOOTER, file=out_fd)
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
27
meson.build
27
meson.build
|
|
@ -1,6 +1,7 @@
|
|||
project('libx52', 'C',
|
||||
license: 'GPL-2.0-only WITH Classpath-exception-2.0',
|
||||
version: '0.3.3')
|
||||
version: '0.3.3',
|
||||
meson_version: '>=0.61')
|
||||
|
||||
dep_libusb = dependency('libusb-1.0', required: true)
|
||||
dep_hidapi = dependency('hidapi-hidraw', required: false)
|
||||
|
|
@ -37,12 +38,12 @@ 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('localedir'))
|
||||
cdata.set_quoted('SYSCONFDIR', get_option('sysconfdir'))
|
||||
cdata.set_quoted('LOCALSTATEDIR', get_option('localstatedir'))
|
||||
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', get_option('nls').enabled())
|
||||
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>'))
|
||||
|
|
@ -73,7 +74,7 @@ if not dep_intl.found() and host_machine.system() == 'darwin'
|
|||
endif
|
||||
|
||||
# # define GETTEXT_PACKAGE
|
||||
if get_option('nls').enabled()
|
||||
if not get_option('nls').disabled()
|
||||
add_project_arguments(
|
||||
'-DGETTEXT_PACKAGE="@0@"'.format(meson.project_name()),
|
||||
language:'C')
|
||||
|
|
@ -81,6 +82,8 @@ if get_option('nls').enabled()
|
|||
subdir('po')
|
||||
endif
|
||||
|
||||
add_project_arguments('-isystem', meson.current_source_dir() / 'sys', language: 'C')
|
||||
|
||||
#######################################################################
|
||||
# Internal dependencies
|
||||
#######################################################################
|
||||
|
|
@ -127,14 +130,20 @@ if doxygen_program.found()
|
|||
configuration: {
|
||||
'PACKAGE_NAME': meson.project_name(),
|
||||
'PACKAGE_VERSION': meson.project_version(),
|
||||
'abs_top_builddir': meson.build_root(),
|
||||
'abs_top_srcdir': meson.source_root(),
|
||||
'abs_top_builddir': meson.project_build_root(),
|
||||
'abs_top_srcdir': meson.project_source_root(),
|
||||
}
|
||||
)
|
||||
|
||||
docs_tgt = custom_target('docs',
|
||||
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
|
||||
|
|
|
|||
|
|
@ -2,10 +2,20 @@ 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')
|
||||
|
|
|
|||
164
po/libx52.pot
164
po/libx52.pot
|
|
@ -6,9 +6,9 @@
|
|||
#, fuzzy
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: libx52 0.3.2\n"
|
||||
"Project-Id-Version: libx52 0.3.3\n"
|
||||
"Report-Msgid-Bugs-To: https://github.com/nirenjan/libx52/issues\n"
|
||||
"POT-Creation-Date: 2026-03-12 08:31-0700\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,88 +513,88 @@ 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 ""
|
||||
|
||||
|
|
@ -658,21 +658,21 @@ msgstr ""
|
|||
msgid "Setting date format to %s"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/x52d_clock.c:173
|
||||
#: daemon/x52d_clock.c:174
|
||||
msgid "Starting X52 clock manager thread"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/x52d_clock.c:184
|
||||
#: daemon/x52d_clock.c:185
|
||||
#, c-format
|
||||
msgid "Error %d retrieving current time: %s"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/x52d_clock.c:205
|
||||
#: daemon/x52d_clock.c:206
|
||||
#, c-format
|
||||
msgid "Error %d initializing clock thread: %s"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/x52d_clock.c:212
|
||||
#: daemon/x52d_clock.c:213
|
||||
msgid "Shutting down X52 clock manager thread"
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -686,31 +686,31 @@ msgstr ""
|
|||
msgid "Short write to client %d; expected %d bytes, wrote %d bytes"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/x52d_command.c:414
|
||||
#: daemon/x52d_command.c:415
|
||||
#, c-format
|
||||
msgid "Error %d during command loop: %s"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/x52d_command.c:441
|
||||
#: daemon/x52d_command.c:442
|
||||
#, c-format
|
||||
msgid "Error creating command socket: %s"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/x52d_command.c:449
|
||||
#: daemon/x52d_command.c:450
|
||||
#, c-format
|
||||
msgid "Error marking command socket as nonblocking: %s"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/x52d_command.c:455
|
||||
#: daemon/x52d_command.c:456
|
||||
#, c-format
|
||||
msgid "Error listening on command socket: %s"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/x52d_command.c:459
|
||||
#: daemon/x52d_command.c:460
|
||||
msgid "Starting command processing thread"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/x52d_command.c:477
|
||||
#: daemon/x52d_command.c:478
|
||||
msgid "Shutting down command processing thread"
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -772,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 ""
|
||||
|
||||
|
|
@ -875,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 ""
|
||||
|
|
@ -916,27 +916,27 @@ msgstr ""
|
|||
msgid "Error setting up notification socket"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/x52d_notify.c:80 daemon/x52d_notify.c:90
|
||||
#: daemon/x52d_notify.c:81 daemon/x52d_notify.c:91
|
||||
#, c-format
|
||||
msgid "Error %d reading from pipe: %s"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/x52d_notify.c:127
|
||||
#: daemon/x52d_notify.c:128
|
||||
#, c-format
|
||||
msgid "Error %d writing notification pipe: %s"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/x52d_notify.c:172
|
||||
#: daemon/x52d_notify.c:174
|
||||
#, c-format
|
||||
msgid "Error %d creating notification pipe: %s"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/x52d_notify.c:181
|
||||
#: daemon/x52d_notify.c:183
|
||||
#, c-format
|
||||
msgid "Error %d initializing notify thread: %s"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/x52d_notify.c:187
|
||||
#: daemon/x52d_notify.c:189
|
||||
#, c-format
|
||||
msgid "Error %d initializing notify listener: %s"
|
||||
msgstr ""
|
||||
|
|
@ -951,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 ""
|
||||
|
|
|
|||
|
|
@ -1,3 +1,14 @@
|
|||
i18n.gettext(meson.project_name(),
|
||||
args: '--directory=' + meson.source_root(),
|
||||
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,
|
||||
)
|
||||
|
|
|
|||
124
po/xx_PL.po
124
po/xx_PL.po
|
|
@ -7,7 +7,7 @@ msgid ""
|
|||
msgstr ""
|
||||
"Project-Id-Version: libx52 0.2.3\n"
|
||||
"Report-Msgid-Bugs-To: https://github.com/nirenjan/libx52/issues\n"
|
||||
"POT-Creation-Date: 2026-03-12 08:31-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"
|
||||
|
|
@ -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,88 +562,88 @@ 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"
|
||||
|
||||
|
|
@ -710,21 +710,21 @@ msgstr "Ettingsay %s ockclay ormatfay otay %s"
|
|||
msgid "Setting date format to %s"
|
||||
msgstr "Ettingsay ateday ormatfay otay %s"
|
||||
|
||||
#: daemon/x52d_clock.c:173
|
||||
#: daemon/x52d_clock.c:174
|
||||
msgid "Starting X52 clock manager thread"
|
||||
msgstr "Artingstay X52 ockclay anagermay eadthray"
|
||||
|
||||
#: daemon/x52d_clock.c:184
|
||||
#: 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:205
|
||||
#: 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:212
|
||||
#: daemon/x52d_clock.c:213
|
||||
msgid "Shutting down X52 clock manager thread"
|
||||
msgstr "Uttingshay ownday X52 ockclay anagermay eadthray"
|
||||
|
||||
|
|
@ -739,31 +739,31 @@ 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:414
|
||||
#: 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:441
|
||||
#: daemon/x52d_command.c:442
|
||||
#, c-format
|
||||
msgid "Error creating command socket: %s"
|
||||
msgstr "Erroray eatingcray ommandcay ocketsay: %s"
|
||||
|
||||
#: daemon/x52d_command.c:449
|
||||
#: daemon/x52d_command.c:450
|
||||
#, c-format
|
||||
msgid "Error marking command socket as nonblocking: %s"
|
||||
msgstr "Erroray arkingmay ommandcay ocketsay asay onblockingnay: %s"
|
||||
|
||||
#: daemon/x52d_command.c:455
|
||||
#: 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:459
|
||||
#: daemon/x52d_command.c:460
|
||||
msgid "Starting command processing thread"
|
||||
msgstr "Artingstay ommandcay ocessingpray eadthray"
|
||||
|
||||
#: daemon/x52d_command.c:477
|
||||
#: daemon/x52d_command.c:478
|
||||
msgid "Shutting down command processing thread"
|
||||
msgstr "Uttingshay ownday ommandcay ocessingpray eadthray"
|
||||
|
||||
|
|
@ -825,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"
|
||||
|
||||
|
|
@ -928,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"
|
||||
|
|
@ -969,27 +969,27 @@ msgstr "Erroray isteninglay onay otificationnay ocketsay: %s"
|
|||
msgid "Error setting up notification socket"
|
||||
msgstr "Erroray ettingsay upay otificationnay ocketsay: %s"
|
||||
|
||||
#: daemon/x52d_notify.c:80 daemon/x52d_notify.c:90
|
||||
#: 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:127
|
||||
#: 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:172
|
||||
#: 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:181
|
||||
#: 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:187
|
||||
#: daemon/x52d_notify.c:189
|
||||
#, c-format
|
||||
msgid "Error %d initializing notify listener: %s"
|
||||
msgstr "Erroray %d initializingay otifynay istenerlay: %s"
|
||||
|
|
@ -1004,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"
|
||||
|
|
|
|||
|
|
@ -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];
|
||||
|
|
|
|||
|
|
@ -21,39 +21,41 @@ level_class = ['nolvl', 'lvl']
|
|||
backtrace_class = ['notr', 'tr']
|
||||
|
||||
test_files = []
|
||||
test_name_template = '@0@-@1@-@2@-@3@'
|
||||
foreach test_type: ['bench', 'test']
|
||||
test_src = test_type + '_pinelog.c'
|
||||
foreach date_arg: [0, 1]
|
||||
date_def = '-DPINELOG_SHOW_DATE=' + date_arg.to_string()
|
||||
date_name = date_arg == 1 ? 'ts' : 'nots'
|
||||
test_name_template = '-@0@-@1@-@2@'
|
||||
foreach date_arg: [0, 1]
|
||||
date_def = '-DPINELOG_SHOW_DATE=' + date_arg.to_string()
|
||||
date_name = date_arg == 1 ? 'ts' : 'nots'
|
||||
|
||||
foreach level_arg: [0, 1]
|
||||
level_def = '-DPINELOG_SHOW_LEVEL=' + level_arg.to_string()
|
||||
level_name = level_arg == 1 ? 'lvl' : 'nolvl'
|
||||
foreach level_arg: [0, 1]
|
||||
level_def = '-DPINELOG_SHOW_LEVEL=' + level_arg.to_string()
|
||||
level_name = level_arg == 1 ? 'lvl' : 'nolvl'
|
||||
|
||||
foreach backtrace_arg: [0, 1]
|
||||
backtrace_def = '-DPINELOG_SHOW_BACKTRACE=' + backtrace_arg.to_string()
|
||||
backtrace_name = backtrace_arg == 1 ? 'tr' : 'notr'
|
||||
foreach backtrace_arg: [0, 1]
|
||||
backtrace_def = '-DPINELOG_SHOW_BACKTRACE=' + backtrace_arg.to_string()
|
||||
backtrace_name = backtrace_arg == 1 ? 'tr' : 'notr'
|
||||
|
||||
test_name = test_name_template.format(test_type,
|
||||
date_name, level_name, backtrace_name)
|
||||
test_exe = executable(test_name, test_src, 'pinelog.c',
|
||||
c_args: [
|
||||
'-DPINELOG_FATAL_STR="F"',
|
||||
'-DPINELOG_ERROR_STR="E"',
|
||||
'-DPINELOG_WARNING_STR="W"',
|
||||
'-DPINELOG_INFO_STR="I"',
|
||||
'-DPINELOG_DEBUG_STR="D"',
|
||||
'-DPINELOG_TRACE_STR="T"',
|
||||
'-DPINELOG_DEFAULT_LEVEL=PINELOG_LVL_TRACE',
|
||||
'-DPINELOG_DEFAULT_STREAM=stderr',
|
||||
'-DPINELOG_TEST',
|
||||
date_def, level_def, backtrace_def
|
||||
])
|
||||
c_args = [
|
||||
'-DPINELOG_FATAL_STR="F"',
|
||||
'-DPINELOG_ERROR_STR="E"',
|
||||
'-DPINELOG_WARNING_STR="W"',
|
||||
'-DPINELOG_INFO_STR="I"',
|
||||
'-DPINELOG_DEBUG_STR="D"',
|
||||
'-DPINELOG_TRACE_STR="T"',
|
||||
'-DPINELOG_DEFAULT_LEVEL=PINELOG_LVL_TRACE',
|
||||
'-DPINELOG_DEFAULT_STREAM=stderr',
|
||||
'-DPINELOG_TEST',
|
||||
date_def, level_def, backtrace_def
|
||||
]
|
||||
test_name = test_name_template.format(
|
||||
date_name, level_name, backtrace_name)
|
||||
|
||||
test(test_name, test_exe, protocol: 'tap')
|
||||
endforeach
|
||||
test_exe = executable('test' + test_name, 'test_pinelog.c', 'pinelog.c',
|
||||
c_args: c_args)
|
||||
test('test' + test_name, test_exe, protocol: 'tap')
|
||||
|
||||
bench_exe = executable('bench' + test_name, 'bench_pinelog.c', 'pinelog.c',
|
||||
c_args: c_args)
|
||||
benchmark('bench' + test_name, bench_exe, protocol: 'tap')
|
||||
endforeach
|
||||
endforeach
|
||||
endforeach
|
||||
|
|
|
|||
|
|
@ -348,6 +348,10 @@ void pinelog_log_message(int module, int level, const char *file, int line, cons
|
|||
#else
|
||||
fprintf(output_stream, "%s:%d ", file, line);
|
||||
#endif
|
||||
#else
|
||||
// Suppress unused parameter warnings
|
||||
(void)file;
|
||||
(void)line;
|
||||
#endif
|
||||
|
||||
/* Set the module name if it is not the root */
|
||||
|
|
|
|||
|
|
@ -36,6 +36,7 @@ static size_t expected_len;
|
|||
|
||||
time_t time(time_t *p)
|
||||
{
|
||||
(void)p;
|
||||
// Override the time function from libc
|
||||
return 1636671600;
|
||||
}
|
||||
|
|
@ -196,7 +197,7 @@ static void tap_bailout(const char *msg)
|
|||
exit(1);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
int main(void)
|
||||
{
|
||||
int fifo_fd[2];
|
||||
int flags;
|
||||
|
|
|
|||
|
|
@ -1,11 +1,10 @@
|
|||
# udev rules
|
||||
if dep_udev.found()
|
||||
if meson.version().version_compare('>= 0.58.0')
|
||||
udev_rules_dir = get_option('udev-rules-dir')
|
||||
if udev_rules_dir == ''
|
||||
udev_dir = dep_udev.get_variable('udevdir', default_value:'/lib/udev')
|
||||
else
|
||||
udev_dir = dep_udev.get_pkgconfig_variable('udevdir', default:'/lib/udev')
|
||||
udev_rules_dir = join_paths(udev_dir, 'rules.d')
|
||||
endif
|
||||
udev_rules_dir = join_paths(udev_dir, 'rules.d')
|
||||
udev_file = configure_file(
|
||||
input: '60-saitek-x52-x52pro.rules.in',
|
||||
output: '60-saitek-x52-x52pro.rules',
|
||||
|
|
|
|||
Loading…
Reference in New Issue