| { | ||
| "git": { | ||
| "sha1": "19b3f78096fc6e3cfe3e45c007161e31b8aa7eb7" | ||
| }, | ||
| "path_in_vcs": "" | ||
| } |
+29
-41
@@ -6,18 +6,12 @@ # This file is automatically @generated by Cargo. | ||
| [[package]] | ||
| name = "autocfg" | ||
| version = "1.1.0" | ||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" | ||
| [[package]] | ||
| name = "bitflags" | ||
| version = "1.3.2" | ||
| version = "2.4.2" | ||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" | ||
| checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf" | ||
| [[package]] | ||
| name = "cc" | ||
| version = "1.0.78" | ||
| version = "1.0.79" | ||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| checksum = "a20104e2335ce8a659d6dd92a51a767a0c062599c73b343fd152cb401e828c3d" | ||
| checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" | ||
@@ -31,4 +25,10 @@ [[package]] | ||
| [[package]] | ||
| name = "hermit-abi" | ||
| version = "0.3.4" | ||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| checksum = "5d3d0e0f38255e7fa3cf31335b3a56f05febd18025f4db5ef7a0cfb4f8da651f" | ||
| [[package]] | ||
| name = "hidapi" | ||
| version = "2.5.0" | ||
| version = "2.5.1" | ||
| dependencies = [ | ||
@@ -45,6 +45,17 @@ "cc", | ||
| [[package]] | ||
| name = "io-lifetimes" | ||
| version = "1.0.11" | ||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2" | ||
| dependencies = [ | ||
| "hermit-abi", | ||
| "libc", | ||
| "windows-sys", | ||
| ] | ||
| [[package]] | ||
| name = "libc" | ||
| version = "0.2.139" | ||
| version = "0.2.152" | ||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79" | ||
| checksum = "13e3bf6590cbc649f4d1a3eefc9d5d6eb746f5200ffb04e5e142700b8faa56e7" | ||
@@ -62,15 +73,6 @@ [[package]] | ||
| [[package]] | ||
| name = "memoffset" | ||
| version = "0.7.1" | ||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| checksum = "5de893c32cde5f383baa4c04c5d6dbdd735cfd4a794b0debdb2bb1b421da5ff4" | ||
| dependencies = [ | ||
| "autocfg", | ||
| ] | ||
| [[package]] | ||
| name = "nix" | ||
| version = "0.26.2" | ||
| version = "0.27.1" | ||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| checksum = "bfdda3d196821d6af13126e40375cdf7da646a96114af134d5f417a9a1dc8e1a" | ||
| checksum = "2eb04e9c688eff1c89d72b407f168cf79bb9e867a9d3323ed6c01519eb9cc053" | ||
| dependencies = [ | ||
@@ -80,14 +82,5 @@ "bitflags", | ||
| "libc", | ||
| "memoffset", | ||
| "pin-utils", | ||
| "static_assertions", | ||
| ] | ||
| [[package]] | ||
| name = "pin-utils" | ||
| version = "0.1.0" | ||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" | ||
| [[package]] | ||
| name = "pkg-config" | ||
@@ -99,13 +92,8 @@ version = "0.3.26" | ||
| [[package]] | ||
| name = "static_assertions" | ||
| version = "1.1.0" | ||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" | ||
| [[package]] | ||
| name = "udev" | ||
| version = "0.7.0" | ||
| version = "0.8.0" | ||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| checksum = "4ebdbbd670373442a12fe9ef7aeb53aec4147a5a27a00bbc3ab639f08f48191a" | ||
| checksum = "50051c6e22be28ee6f217d50014f3bc29e81c20dc66ff7ca0d5c5226e1dcc5a1" | ||
| dependencies = [ | ||
| "io-lifetimes", | ||
| "libc", | ||
@@ -112,0 +100,0 @@ "libudev-sys", |
+30
-3
@@ -15,3 +15,3 @@ # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO | ||
| name = "hidapi" | ||
| version = "2.5.0" | ||
| version = "2.5.1" | ||
| authors = [ | ||
@@ -27,2 +27,24 @@ "Roland Ruckerbauer <roland.rucky@gmail.com>", | ||
| links = "hidapi" | ||
| include = [ | ||
| "README.md", | ||
| "LICENSE.txt", | ||
| "build.rs", | ||
| "/src", | ||
| "/etc/hidapi/CMakeLists.txt", | ||
| "/etc/hidapi/LICENSE*", | ||
| "/etc/hidapi/VERSION", | ||
| "/etc/hidapi/hidapi", | ||
| "/etc/hidapi/libusb", | ||
| "/etc/hidapi/src", | ||
| "/etc/hidapi/udev", | ||
| "/etc/hidapi/linux/CMakeLists.txt", | ||
| "/etc/hidapi/linux/*.c", | ||
| "/etc/hidapi/linux/*.h", | ||
| "/etc/hidapi/mac/CMakeLists.txt", | ||
| "/etc/hidapi/mac/*.c", | ||
| "/etc/hidapi/mac/*.h", | ||
| "/etc/hidapi/windows/CMakeLists.txt", | ||
| "/etc/hidapi/windows/*.c", | ||
| "/etc/hidapi/windows/*.h", | ||
| ] | ||
| description = "Rust-y wrapper around hidapi" | ||
@@ -88,7 +110,12 @@ documentation = "https://docs.rs/hidapi" | ||
| [target."cfg(target_os = \"linux\")".dependencies.nix] | ||
| version = "0.26" | ||
| version = "0.27" | ||
| features = [ | ||
| "fs", | ||
| "ioctl", | ||
| "poll", | ||
| ] | ||
| optional = true | ||
| [target."cfg(target_os = \"linux\")".dependencies.udev] | ||
| version = "0.7" | ||
| version = "0.8" | ||
| optional = true | ||
@@ -95,0 +122,0 @@ |
+1
-1
@@ -280,3 +280,3 @@ // ************************************************************************** | ||
| if device.is_null() { | ||
| match self.check_error() { | ||
| match HidApiBackend::check_error() { | ||
| Ok(err) => Err(err), | ||
@@ -283,0 +283,0 @@ Err(e) => Err(e), |
@@ -492,3 +492,3 @@ //! This backend uses libudev to discover devices and then talks to hidraw directly | ||
| fn read_timeout(&self, buf: &mut [u8], timeout: i32) -> HidResult<usize> { | ||
| let pollfd = PollFd::new(self.fd.as_raw_fd(), PollFlags::POLLIN); | ||
| let pollfd = PollFd::new(&self.fd, PollFlags::POLLIN); | ||
| let res = poll(&mut [pollfd], timeout)?; | ||
@@ -495,0 +495,0 @@ |
| name: Rust | ||
| on: | ||
| push: | ||
| branches: [ master ] | ||
| pull_request: | ||
| branches: [ master ] | ||
| env: | ||
| CARGO_TERM_COLOR: always | ||
| jobs: | ||
| build-linux: | ||
| runs-on: ubuntu-latest | ||
| env: | ||
| DEBIAN_FRONTEND: noninteractive | ||
| strategy: | ||
| fail-fast: false # don't give up on the whole matrix if one variant fails | ||
| matrix: | ||
| linkage: ["static", "shared"] | ||
| library: ["hidraw", "libusb"] | ||
| steps: | ||
| - name: checkout repository and submodules | ||
| uses: actions/checkout@v2 | ||
| with: | ||
| submodules: recursive | ||
| - name: Install dependencies | ||
| run: | | ||
| sudo apt-get update -y | ||
| sudo apt-get install -y cmake libudev-dev libumockdev-dev umockdev | ||
| - name: Build libusb | ||
| run: | | ||
| git clone https://github.com/libusb/libusb.git ./etc/libusb/ | ||
| cd ./etc/libusb/ | ||
| ./autogen.sh | ||
| make | ||
| sudo make install | ||
| - name: Build hidapi | ||
| run: | | ||
| cd ./etc/hidapi/ | ||
| mkdir ./build/ | ||
| cd ./build/ | ||
| cmake .. | ||
| make | ||
| sudo make install | ||
| - name: List pkgconfig definitions | ||
| run: grep -RHn ^ /usr/local/lib/pkgconfig | ||
| - name: Build | ||
| run: cargo build --no-default-features --features linux-${{ matrix.linkage }}-${{ matrix.library }} --verbose | ||
| - name: Run tests | ||
| run: cargo test --no-default-features --features linux-${{ matrix.linkage }}-${{ matrix.library }} --verbose | ||
| build-linux-native: | ||
| runs-on: ubuntu-latest | ||
| env: | ||
| DEBIAN_FRONTEND: noninteractive | ||
| steps: | ||
| - name: checkout repository and submodules | ||
| uses: actions/checkout@v2 | ||
| with: | ||
| submodules: recursive | ||
| - name: Install dependencies | ||
| run: | | ||
| sudo apt-get update -y | ||
| sudo apt-get install -y libudev-dev | ||
| - name: Build | ||
| run: cargo build --no-default-features --features linux-native --verbose | ||
| - name: Run tests | ||
| run: cargo test --no-default-features --features linux-native --verbose | ||
| build-windows: | ||
| runs-on: windows-latest | ||
| steps: | ||
| - name: checkout repository and submodules | ||
| uses: actions/checkout@v2 | ||
| with: | ||
| submodules: recursive | ||
| - name: Install rust environment | ||
| run: | | ||
| curl.exe --proto "=https" --tlsv1.2 -L -o rustup-init.exe https://static.rust-lang.org/rustup/dist/x86_64-pc-windows-msvc/rustup-init.exe | ||
| .\rustup-init.exe -y | ||
| - name: Build | ||
| run: cargo build --no-default-features --verbose | ||
| - name: Run tests | ||
| run: cargo test --no-default-features --verbose | ||
| build-windows-native: | ||
| runs-on: windows-latest | ||
| steps: | ||
| - name: checkout repository and submodules | ||
| uses: actions/checkout@v2 | ||
| with: | ||
| submodules: recursive | ||
| - name: Install rust environment | ||
| run: | | ||
| curl.exe --proto "=https" --tlsv1.2 -L -o rustup-init.exe https://static.rust-lang.org/rustup/dist/x86_64-pc-windows-msvc/rustup-init.exe | ||
| .\rustup-init.exe -y | ||
| - name: Build | ||
| run: cargo build --no-default-features --features windows-native --verbose | ||
| - name: Run tests | ||
| run: cargo test --no-default-features --features windows-native --verbose | ||
| build-macos: | ||
| runs-on: macos-latest | ||
| steps: | ||
| - name: checkout repository and submodules | ||
| uses: actions/checkout@v2 | ||
| with: | ||
| submodules: recursive | ||
| - name: Install rust environment | ||
| run: | | ||
| curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y | ||
| - name: Build | ||
| run: cargo build --no-default-features --verbose | ||
| - name: Run tests | ||
| run: cargo test --no-default-features --verbose | ||
| fmt-check: | ||
| runs-on: ubuntu-latest | ||
| env: | ||
| DEBIAN_FRONTEND: noninteractive | ||
| steps: | ||
| - name: checkout repository and submodules | ||
| uses: actions/checkout@v2 | ||
| with: | ||
| submodules: recursive | ||
| - name: fmt check | ||
| run: cargo fmt --check |
| target | ||
| Cargo.lock | ||
| .idea/ | ||
| hidapi-rs.iml |
| [submodule "etc/hidapi"] | ||
| path = etc/hidapi | ||
| url = https://github.com/libusb/hidapi.git |
-27
| language: rust | ||
| matrix: | ||
| include: | ||
| - env: TARGET=x86_64-unknown-linux-gnu FEATURE_FLAGS="linux-static-libusb" | ||
| - env: TARGET=x86_64-unknown-linux-gnu FEATURE_FLAGS="linux-static-hidraw" | ||
| - env: TARGET=x86_64-unknown-linux-gnu FEATURE_FLAGS="linux-shared-libusb" | ||
| - env: TARGET=x86_64-unknown-linux-gnu FEATURE_FLAGS="linux-shared-hidraw" | ||
| - env: TARGET=x86_64-apple-darwin | ||
| os: osx | ||
| script: | ||
| - cargo build --verbose --no-default-features --features="${FEATURE_FLAGS}" | ||
| - cargo test --verbose --no-default-features --features="${FEATURE_FLAGS}" | ||
| addons: | ||
| apt: | ||
| packages: | ||
| - libusb-1.0-0-dev | ||
| - libudev-dev | ||
| - libhidapi-dev |
| { | ||
| "rust-analyzer.cargo.noDefaultFeatures": true, | ||
| "rust-analyzer.cargo.features": ["linux-native"] | ||
| } |
| environment: | ||
| matrix: | ||
| - BUILD_ENV: msbuild | ||
| arch: x64 | ||
| - BUILD_ENV: msbuild | ||
| arch: Win32 | ||
| - BUILD_ENV: cygwin | ||
| for: | ||
| - | ||
| matrix: | ||
| only: | ||
| - BUILD_ENV: msbuild | ||
| os: Visual Studio 2015 | ||
| build_script: | ||
| - cmd: msbuild .\windows\hidapi.sln /p:Configuration=Release /p:Platform=%arch% /logger:"C:\Program Files\AppVeyor\BuildAgent\Appveyor.MSBuildLogger.dll" | ||
| - | ||
| matrix: | ||
| only: | ||
| - BUILD_ENV: cygwin | ||
| os: Visual Studio 2022 | ||
| install: | ||
| - cmd: C:\cygwin64\setup-x86_64.exe --quiet-mode --no-shortcuts --upgrade-also --packages autoconf,automake | ||
| build_script: | ||
| - cmd: C:\cygwin64\bin\bash -exlc "cd $APPVEYOR_BUILD_FOLDER; ./bootstrap; ./configure; make" |
| image: freebsd/latest | ||
| packages: | ||
| - autoconf | ||
| - automake | ||
| - gmake | ||
| - libiconv | ||
| - libtool | ||
| - pkgconf | ||
| - cmake | ||
| - ninja | ||
| sources: | ||
| - https://github.com/libusb/hidapi | ||
| tasks: | ||
| - configure: | | ||
| cd hidapi | ||
| echo Configure Autotools build | ||
| ./bootstrap | ||
| ./configure | ||
| echo Configure CMake build | ||
| mkdir -p build install_cmake | ||
| cmake -GNinja -B build -S . -DCMAKE_INSTALL_PREFIX=install_cmake | ||
| - build-autotools: | | ||
| cd hidapi | ||
| make | ||
| make DESTDIR=$PWD/root install | ||
| make clean | ||
| - build-cmake: | | ||
| cd hidapi/build | ||
| ninja | ||
| ninja install | ||
| ninja clean | ||
| - build-manual: | | ||
| cd hidapi/libusb | ||
| gmake -f Makefile-manual |
| image: netbsd/latest | ||
| packages: | ||
| - cmake | ||
| - pkgconf | ||
| - libusb1 | ||
| - libiconv | ||
| sources: | ||
| - https://github.com/libusb/hidapi | ||
| tasks: | ||
| - configure: | | ||
| cd hidapi | ||
| mkdir -p build install | ||
| cmake -B build -S . -DCMAKE_INSTALL_PREFIX=install | ||
| - build: | | ||
| cd hidapi/build | ||
| make | ||
| make install | ||
| make clean |
| image: openbsd/latest | ||
| packages: | ||
| - cmake | ||
| - pkgconf | ||
| - libusb1-- | ||
| - libiconv | ||
| - ninja | ||
| sources: | ||
| - https://github.com/libusb/hidapi | ||
| tasks: | ||
| - configure: | | ||
| cd hidapi | ||
| mkdir -p build install | ||
| cmake -GNinja -B build -S . -DCMAKE_INSTALL_PREFIX=install | ||
| - build: | | ||
| cd hidapi/build | ||
| ninja | ||
| ninja install | ||
| ninja clean |
| alpine_task: | ||
| container: | ||
| image: alpine:latest | ||
| install_script: apk add autoconf automake g++ gcc libusb-dev libtool linux-headers eudev-dev make musl-dev | ||
| script: | ||
| - ./bootstrap | ||
| - ./configure || { cat config.log; exit 1; } | ||
| - make | ||
| - make install | ||
| freebsd11_task: | ||
| freebsd_instance: | ||
| image: freebsd-11-2-release-amd64 | ||
| install_script: | ||
| - pkg install -y | ||
| autoconf automake libiconv libtool pkgconf | ||
| script: | ||
| - ./bootstrap | ||
| - ./configure || { cat config.log; exit 1; } | ||
| - make | ||
| - make install | ||
| freebsd12_task: | ||
| freebsd_instance: | ||
| image: freebsd-12-1-release-amd64 | ||
| install_script: | ||
| - pkg install -y | ||
| autoconf automake libiconv libtool pkgconf | ||
| script: | ||
| - ./bootstrap | ||
| - ./configure || { cat config.log; exit 1; } | ||
| - make | ||
| - make install |
| * text=auto | ||
| *.sln text eol=crlf | ||
| *.vcproj text eol=crlf | ||
| bootstrap text eol=lf | ||
| configure.ac text eol=lf |
| name: GitHub Builds | ||
| on: [push, pull_request] | ||
| env: | ||
| NIX_COMPILE_FLAGS: -Wall -Wextra -pedantic -Werror | ||
| MSVC_COMPILE_FLAGS: /W4 /WX | ||
| jobs: | ||
| macos-automake: | ||
| runs-on: macos-latest | ||
| steps: | ||
| - uses: actions/checkout@v3 | ||
| - name: Install build tools | ||
| run: brew install autoconf automake libtool | ||
| - name: Configure Automake | ||
| run: | | ||
| ./bootstrap | ||
| ./configure --prefix=$(pwd)/install | ||
| - name: Build Automake | ||
| run: | | ||
| make | ||
| make install | ||
| - name: Clean build | ||
| run: make clean | ||
| - name: Build Manual makefile | ||
| working-directory: mac | ||
| run: make -f Makefile-manual | ||
| macos-cmake: | ||
| runs-on: macos-latest | ||
| steps: | ||
| - uses: actions/checkout@v3 | ||
| with: | ||
| path: hidapisrc | ||
| - name: Install dependencies | ||
| run: brew install meson ninja | ||
| - name: Configure CMake | ||
| run: | | ||
| rm -rf build install | ||
| cmake -B build/shared -S hidapisrc -DCMAKE_BUILD_TYPE=RelWithDebInfo -DHIDAPI_ENABLE_ASAN=ON -DCMAKE_INSTALL_PREFIX=install/shared -DHIDAPI_BUILD_HIDTEST=ON "-DCMAKE_C_FLAGS=${NIX_COMPILE_FLAGS}" | ||
| cmake -B build/static -S hidapisrc -DCMAKE_BUILD_TYPE=RelWithDebInfo -DHIDAPI_ENABLE_ASAN=ON -DCMAKE_INSTALL_PREFIX=install/static -DBUILD_SHARED_LIBS=FALSE -DHIDAPI_BUILD_HIDTEST=ON "-DCMAKE_C_FLAGS=${NIX_COMPILE_FLAGS}" | ||
| cmake -B build/framework -S hidapisrc -DCMAKE_BUILD_TYPE=RelWithDebInfo -DHIDAPI_ENABLE_ASAN=ON -DCMAKE_INSTALL_PREFIX=install/framework -DCMAKE_FRAMEWORK=ON -DHIDAPI_BUILD_HIDTEST=ON "-DCMAKE_C_FLAGS=${NIX_COMPILE_FLAGS}" | ||
| - name: Build CMake Shared | ||
| working-directory: build/shared | ||
| run: make install | ||
| - name: Build CMake Static | ||
| working-directory: build/static | ||
| run: make install | ||
| - name: Build CMake Framework | ||
| working-directory: build/framework | ||
| run: make install | ||
| - name: Check artifacts | ||
| uses: andstor/file-existence-action@v2 | ||
| with: | ||
| files: "install/shared/lib/libhidapi.dylib, \ | ||
| install/shared/include/hidapi/hidapi.h, \ | ||
| install/shared/include/hidapi/hidapi_darwin.h, \ | ||
| install/static/lib/libhidapi.a, \ | ||
| install/static/include/hidapi/hidapi.h, \ | ||
| install/static/include/hidapi/hidapi_darwin.h, \ | ||
| install/framework/lib/hidapi.framework/hidapi, \ | ||
| install/framework/lib/hidapi.framework/Headers/hidapi.h, \ | ||
| install/framework/lib/hidapi.framework/Headers/hidapi_darwin.h" | ||
| fail: true | ||
| - name: Check CMake Export Package Shared | ||
| run: | | ||
| cmake \ | ||
| -B build/shared_test \ | ||
| -S hidapisrc/hidtest \ | ||
| -Dhidapi_ROOT=install/shared \ | ||
| -DCMAKE_INSTALL_PREFIX=install/shared_test \ | ||
| "-DCMAKE_C_FLAGS=${NIX_COMPILE_FLAGS}" | ||
| cd build/shared_test | ||
| make install | ||
| - name: Check CMake Export Package Static | ||
| run: | | ||
| cmake \ | ||
| -B build/static_test \ | ||
| -S hidapisrc/hidtest \ | ||
| -Dhidapi_ROOT=install/static \ | ||
| -DCMAKE_INSTALL_PREFIX=install/static_test \ | ||
| "-DCMAKE_C_FLAGS=${NIX_COMPILE_FLAGS}" | ||
| cd build/static_test | ||
| make install | ||
| - name: Check Meson build | ||
| run: | | ||
| meson setup build_meson hidapisrc | ||
| cd build_meson | ||
| ninja | ||
| ubuntu-cmake: | ||
| runs-on: ubuntu-latest | ||
| steps: | ||
| - uses: actions/checkout@v3 | ||
| with: | ||
| path: hidapisrc | ||
| - name: Install dependencies | ||
| run: | | ||
| sudo apt update | ||
| sudo apt install libudev-dev libusb-1.0-0-dev python3-pip ninja-build | ||
| sudo -H pip3 install meson | ||
| - name: Configure CMake | ||
| run: | | ||
| rm -rf build install | ||
| cmake -B build/shared -S hidapisrc -DCMAKE_BUILD_TYPE=RelWithDebInfo -DHIDAPI_ENABLE_ASAN=ON -DCMAKE_INSTALL_PREFIX=install/shared -DHIDAPI_BUILD_HIDTEST=ON "-DCMAKE_C_FLAGS=${NIX_COMPILE_FLAGS}" | ||
| cmake -B build/static -S hidapisrc -DCMAKE_BUILD_TYPE=RelWithDebInfo -DHIDAPI_ENABLE_ASAN=ON -DCMAKE_INSTALL_PREFIX=install/static -DBUILD_SHARED_LIBS=FALSE -DHIDAPI_BUILD_HIDTEST=ON "-DCMAKE_C_FLAGS=${NIX_COMPILE_FLAGS}" | ||
| - name: Build CMake Shared | ||
| working-directory: build/shared | ||
| run: make install | ||
| - name: Build CMake Static | ||
| working-directory: build/static | ||
| run: make install | ||
| - name: Check artifacts | ||
| uses: andstor/file-existence-action@v2 | ||
| with: | ||
| files: "install/shared/lib/libhidapi-libusb.so, \ | ||
| install/shared/lib/libhidapi-hidraw.so, \ | ||
| install/shared/include/hidapi/hidapi.h, \ | ||
| install/shared/include/hidapi/hidapi_libusb.h, \ | ||
| install/static/lib/libhidapi-libusb.a, \ | ||
| install/static/lib/libhidapi-hidraw.a, \ | ||
| install/static/include/hidapi/hidapi.h, \ | ||
| install/static/include/hidapi/hidapi_libusb.h" | ||
| fail: true | ||
| - name: Check CMake Export Package Shared | ||
| run: | | ||
| cmake \ | ||
| -B build/shared_test \ | ||
| -S hidapisrc/hidtest \ | ||
| -Dhidapi_ROOT=install/shared \ | ||
| -DCMAKE_INSTALL_PREFIX=install/shared_test \ | ||
| "-DCMAKE_C_FLAGS=${NIX_COMPILE_FLAGS}" | ||
| cd build/shared_test | ||
| make install | ||
| - name: Check CMake Export Package Static | ||
| run: | | ||
| cmake \ | ||
| -B build/static_test \ | ||
| -S hidapisrc/hidtest \ | ||
| -Dhidapi_ROOT=install/static \ | ||
| -DCMAKE_INSTALL_PREFIX=install/static_test \ | ||
| "-DCMAKE_C_FLAGS=${NIX_COMPILE_FLAGS}" | ||
| cd build/static_test | ||
| make install | ||
| - name: Check Meson build | ||
| run: | | ||
| meson setup build_meson hidapisrc | ||
| cd build_meson | ||
| ninja | ||
| windows-cmake: | ||
| runs-on: windows-latest | ||
| steps: | ||
| - uses: actions/checkout@v3 | ||
| with: | ||
| path: hidapisrc | ||
| - name: Install dependencies | ||
| shell: cmd | ||
| run: | | ||
| choco install ninja | ||
| pip3 install meson | ||
| refreshenv | ||
| - name: Configure CMake MSVC | ||
| shell: cmd | ||
| run: | | ||
| cmake -B build\msvc -S hidapisrc -DCMAKE_BUILD_TYPE=RelWithDebInfo -DHIDAPI_WITH_TESTS=ON -DHIDAPI_BUILD_PP_DATA_DUMP=ON -DHIDAPI_ENABLE_ASAN=ON -DCMAKE_INSTALL_PREFIX=install\msvc -DHIDAPI_BUILD_HIDTEST=ON "-DCMAKE_C_FLAGS=%MSVC_COMPILE_FLAGS%" | ||
| - name: Build CMake MSVC | ||
| working-directory: build/msvc | ||
| run: cmake --build . --config RelWithDebInfo --target install | ||
| - name: Check artifacts MSVC | ||
| uses: andstor/file-existence-action@v2 | ||
| with: | ||
| files: "install/msvc/lib/hidapi.lib, \ | ||
| install/msvc/bin/hidapi.dll, \ | ||
| install/msvc/include/hidapi/hidapi.h, \ | ||
| install/msvc/include/hidapi/hidapi_winapi.h" | ||
| fail: true | ||
| - name: Check CMake Export Package | ||
| shell: cmd | ||
| run: | | ||
| cmake ^ | ||
| -B build\msvc_test ^ | ||
| -S hidapisrc\hidtest ^ | ||
| -Dhidapi_ROOT=install\msvc ^ | ||
| -DCMAKE_INSTALL_PREFIX=install\msvc_test ^ | ||
| "-DCMAKE_C_FLAGS=%MSVC_COMPILE_FLAGS%" | ||
| cd build\msvc_test | ||
| cmake --build . --target install | ||
| - name: Run CTest MSVC | ||
| shell: cmd | ||
| working-directory: build/msvc | ||
| run: ctest -C RelWithDebInfo --rerun-failed --output-on-failure | ||
| - name: Configure CMake NMake | ||
| shell: cmd | ||
| run: | | ||
| call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat" | ||
| cmake -G"NMake Makefiles" -B build\nmake -S hidapisrc -DCMAKE_BUILD_TYPE=RelWithDebInfo -DHIDAPI_WITH_TESTS=ON -DHIDAPI_BUILD_PP_DATA_DUMP=ON -DHIDAPI_ENABLE_ASAN=ON -DCMAKE_INSTALL_PREFIX=install\nmake -DHIDAPI_BUILD_HIDTEST=ON "-DCMAKE_C_FLAGS=%MSVC_COMPILE_FLAGS%" | ||
| - name: Build CMake NMake | ||
| working-directory: build\nmake | ||
| shell: cmd | ||
| run: | | ||
| call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat" | ||
| nmake install | ||
| - name: Check artifacts NMake | ||
| uses: andstor/file-existence-action@v2 | ||
| with: | ||
| files: "install/nmake/lib/hidapi.lib, \ | ||
| install/nmake/bin/hidapi.dll, \ | ||
| install/nmake/include/hidapi/hidapi.h, \ | ||
| install/nmake/include/hidapi/hidapi_winapi.h" | ||
| fail: true | ||
| - name: Check CMake Export Package NMake | ||
| shell: cmd | ||
| run: | | ||
| call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat" | ||
| cmake ^ | ||
| -G"NMake Makefiles" ^ | ||
| -B build\nmake_test ^ | ||
| -S hidapisrc\hidtest ^ | ||
| -Dhidapi_ROOT=install\nmake ^ | ||
| -DCMAKE_INSTALL_PREFIX=install\nmake_test ^ | ||
| "-DCMAKE_C_FLAGS=%MSVC_COMPILE_FLAGS%" | ||
| cd build\nmake_test | ||
| nmake install | ||
| - name: Run CTest NMake | ||
| working-directory: build\nmake | ||
| run: ctest --rerun-failed --output-on-failure | ||
| - name: Configure CMake MinGW | ||
| shell: cmd | ||
| run: | | ||
| cmake -G"MinGW Makefiles" -B build\mingw -S hidapisrc -DCMAKE_BUILD_TYPE=RelWithDebInfo -DHIDAPI_WITH_TESTS=ON -DHIDAPI_BUILD_PP_DATA_DUMP=ON -DCMAKE_INSTALL_PREFIX=install\mingw -DHIDAPI_BUILD_HIDTEST=ON "-DCMAKE_C_FLAGS=%NIX_COMPILE_FLAGS%" | ||
| - name: Build CMake MinGW | ||
| working-directory: build\mingw | ||
| run: cmake --build . --target install | ||
| - name: Check artifacts MinGW | ||
| uses: andstor/file-existence-action@v2 | ||
| with: | ||
| files: "install/mingw/lib/libhidapi.dll.a, \ | ||
| install/mingw/bin/libhidapi.dll, \ | ||
| install/mingw/include/hidapi/hidapi.h, \ | ||
| install/mingw/include/hidapi/hidapi_winapi.h" | ||
| fail: true | ||
| - name: Check CMake Export Package MinGW | ||
| shell: cmd | ||
| run: | | ||
| cmake ^ | ||
| -G"MinGW Makefiles" ^ | ||
| -B build\mingw_test ^ | ||
| -S hidapisrc\hidtest ^ | ||
| -Dhidapi_ROOT=install\mingw ^ | ||
| -DCMAKE_INSTALL_PREFIX=install\mingw_test ^ | ||
| "-DCMAKE_C_FLAGS=%NIX_COMPILE_FLAGS%" | ||
| cd build\mingw_test | ||
| cmake --build . --target install | ||
| - name: Run CTest MinGW | ||
| working-directory: build\mingw | ||
| run: ctest --rerun-failed --output-on-failure | ||
| - name: Check Meson build | ||
| shell: cmd | ||
| run: | | ||
| call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat" | ||
| meson setup build_meson hidapisrc | ||
| cd build_meson | ||
| ninja | ||
| windows-msbuild: | ||
| runs-on: windows-latest | ||
| steps: | ||
| - uses: actions/checkout@v3 | ||
| - uses: microsoft/setup-msbuild@v1.1 | ||
| - name: MSBuild x86 | ||
| run: msbuild windows\hidapi.sln /p:Configuration=Release /p:Platform=Win32 | ||
| - name: Check artifacts x86 | ||
| uses: andstor/file-existence-action@v2 | ||
| with: | ||
| files: "windows/Release/hidapi.dll, windows/Release/hidapi.lib, windows/Release/hidapi.pdb" | ||
| fail: true | ||
| - name: MSBuild x64 | ||
| run: msbuild windows\hidapi.sln /p:Configuration=Release /p:Platform=x64 | ||
| - name: Check artifacts x64 | ||
| uses: andstor/file-existence-action@v2 | ||
| with: | ||
| files: "windows/x64/Release/hidapi.dll, windows/x64/Release/hidapi.lib, windows/x64/Release/hidapi.pdb" | ||
| fail: true | ||
| - name: Gather artifacts | ||
| run: | | ||
| md artifacts | ||
| md artifacts\x86 | ||
| md artifacts\x64 | ||
| md artifacts\include | ||
| Copy-Item "windows\Release\hidapi.dll","windows\Release\hidapi.lib","windows\Release\hidapi.pdb" -Destination "artifacts\x86" | ||
| Copy-Item "windows\x64\Release\hidapi.dll","windows\x64\Release\hidapi.lib","windows\x64\Release\hidapi.pdb" -Destination "artifacts\x64" | ||
| Copy-Item "hidapi\hidapi.h","windows\hidapi_winapi.h" -Destination "artifacts\include" | ||
| - name: Upload artifacts | ||
| uses: actions/upload-artifact@v3 | ||
| with: | ||
| name: hidapi-win | ||
| path: artifacts/ | ||
| retention-days: ${{ (github.event_name == 'pull_request' || github.ref_name != 'master') && 7 || 90 }} | ||
| fedora-mingw: | ||
| runs-on: ubuntu-latest | ||
| container: fedora:latest | ||
| steps: | ||
| - uses: actions/checkout@v3 | ||
| with: | ||
| path: hidapisrc | ||
| - name: Install dependencies | ||
| run: sudo dnf install -y autoconf automake libtool mingw64-gcc cmake ninja-build make | ||
| - name: Configure CMake | ||
| run: | | ||
| rm -rf build install | ||
| mingw64-cmake -B build/shared-cmake -S hidapisrc -GNinja -DCMAKE_BUILD_TYPE=RelWithDebInfo -DCMAKE_INSTALL_PREFIX=install/shared-cmake -DHIDAPI_BUILD_HIDTEST=ON "-DCMAKE_C_FLAGS=${NIX_COMPILE_FLAGS}" | ||
| mingw64-cmake -B build/static-cmake -S hidapisrc -GNinja -DCMAKE_BUILD_TYPE=RelWithDebInfo -DCMAKE_INSTALL_PREFIX=install/static-cmake -DBUILD_SHARED_LIBS=FALSE -DHIDAPI_BUILD_HIDTEST=ON "-DCMAKE_C_FLAGS=${NIX_COMPILE_FLAGS}" | ||
| - name: Configure Automake | ||
| working-directory: hidapisrc | ||
| run: | | ||
| ./bootstrap | ||
| mingw64-configure | ||
| - name: Build CMake Shared | ||
| working-directory: build/shared-cmake | ||
| run: ninja install | ||
| - name: Build CMake Static | ||
| working-directory: build/static-cmake | ||
| run: ninja install | ||
| - name: Build Automake | ||
| working-directory: hidapisrc | ||
| run: | | ||
| make | ||
| make DESTDIR=$PWD/../install/automake install | ||
| make clean | ||
| - name: Build manual Makefile | ||
| working-directory: hidapisrc/windows | ||
| run: make -f Makefile-manual OS=MINGW CC=x86_64-w64-mingw32-gcc | ||
| - name: Check artifacts | ||
| uses: andstor/file-existence-action@v2 | ||
| with: | ||
| files: "install/shared-cmake/bin/libhidapi.dll, \ | ||
| install/shared-cmake/lib/libhidapi.dll.a, \ | ||
| install/shared-cmake/include/hidapi/hidapi.h, \ | ||
| install/shared-cmake/include/hidapi/hidapi_winapi.h, \ | ||
| install/static-cmake/lib/libhidapi.a, \ | ||
| install/static-cmake/include/hidapi/hidapi.h, \ | ||
| install/static-cmake/include/hidapi/hidapi_winapi.h" | ||
| fail: true | ||
| - name: Check CMake Export Package Shared | ||
| run: | | ||
| mingw64-cmake \ | ||
| -GNinja \ | ||
| -B build/shared_test \ | ||
| -S hidapisrc/hidtest \ | ||
| -Dhidapi_DIR=$PWD/install/shared-cmake/lib/cmake/hidapi \ | ||
| -DCMAKE_INSTALL_PREFIX=install/shared_test \ | ||
| "-DCMAKE_C_FLAGS=${NIX_COMPILE_FLAGS}" | ||
| cd build/shared_test | ||
| ninja install | ||
| - name: Check CMake Export Package Static | ||
| run: | | ||
| mingw64-cmake \ | ||
| -GNinja \ | ||
| -B build/static_test \ | ||
| -S hidapisrc/hidtest \ | ||
| -Dhidapi_DIR=$PWD/install/static-cmake/lib/cmake/hidapi \ | ||
| -DCMAKE_INSTALL_PREFIX=install/static_test \ | ||
| "-DCMAKE_C_FLAGS=${NIX_COMPILE_FLAGS}" | ||
| cd build/static_test | ||
| ninja install | ||
| archlinux: | ||
| runs-on: ubuntu-latest | ||
| container: archlinux:latest | ||
| steps: | ||
| - uses: actions/checkout@v3 | ||
| with: | ||
| path: hidapisrc | ||
| - name: Install dependencies | ||
| run: | | ||
| pacman -Sy | ||
| pacman -S --noconfirm gcc pkg-config autoconf automake libtool libusb libudev0 cmake make | ||
| - name: Configure CMake | ||
| run: | | ||
| rm -rf build install | ||
| cmake -B build/shared-cmake -S hidapisrc -DCMAKE_BUILD_TYPE=RelWithDebInfo -DCMAKE_INSTALL_PREFIX=install/shared-cmake -DHIDAPI_BUILD_HIDTEST=ON "-DCMAKE_C_FLAGS=${NIX_COMPILE_FLAGS}" | ||
| cmake -B build/static-cmake -S hidapisrc -DCMAKE_BUILD_TYPE=RelWithDebInfo -DCMAKE_INSTALL_PREFIX=install/static-cmake -DBUILD_SHARED_LIBS=FALSE -DHIDAPI_BUILD_HIDTEST=ON "-DCMAKE_C_FLAGS=${NIX_COMPILE_FLAGS}" | ||
| - name: Configure Automake | ||
| working-directory: hidapisrc | ||
| run: | | ||
| ./bootstrap | ||
| ./configure | ||
| - name: Build CMake Shared | ||
| working-directory: build/shared-cmake | ||
| run: make install | ||
| - name: Build CMake Static | ||
| working-directory: build/static-cmake | ||
| run: make install | ||
| - name: Build Automake | ||
| working-directory: hidapisrc | ||
| run: | | ||
| make | ||
| make DESTDIR=$PWD/../install/automake install | ||
| make clean | ||
| - name: Build manual Makefile | ||
| run: | | ||
| cd hidapisrc/linux | ||
| make -f Makefile-manual | ||
| cd ../libusb | ||
| make -f Makefile-manual | ||
| - name: Check artifacts | ||
| uses: andstor/file-existence-action@v2 | ||
| with: | ||
| files: "install/shared-cmake/lib/libhidapi-libusb.so, \ | ||
| install/shared-cmake/lib/libhidapi-hidraw.so, \ | ||
| install/shared-cmake/include/hidapi/hidapi.h, \ | ||
| install/shared-cmake/include/hidapi/hidapi_libusb.h, \ | ||
| install/static-cmake/lib/libhidapi-libusb.a, \ | ||
| install/static-cmake/lib/libhidapi-hidraw.a, \ | ||
| install/static-cmake/include/hidapi/hidapi.h, \ | ||
| install/static-cmake/include/hidapi/hidapi_libusb.h" | ||
| fail: true | ||
| - name: Check CMake Export Package Shared | ||
| run: | | ||
| cmake \ | ||
| -B build/shared_test \ | ||
| -S hidapisrc/hidtest \ | ||
| -Dhidapi_ROOT=install/shared-cmake \ | ||
| -DCMAKE_INSTALL_PREFIX=install/shared_test \ | ||
| "-DCMAKE_C_FLAGS=${NIX_COMPILE_FLAGS}" | ||
| cd build/shared_test | ||
| make install | ||
| - name: Check CMake Export Package Static | ||
| run: | | ||
| cmake \ | ||
| -B build/static_test \ | ||
| -S hidapisrc/hidtest \ | ||
| -Dhidapi_ROOT=install/static-cmake \ | ||
| -DCMAKE_INSTALL_PREFIX=install/static_test \ | ||
| "-DCMAKE_C_FLAGS=${NIX_COMPILE_FLAGS}" | ||
| cd build/static_test | ||
| make install | ||
| alpine: | ||
| runs-on: ubuntu-latest | ||
| container: alpine:edge | ||
| env: | ||
| # A bug in musl: https://www.openwall.com/lists/musl/2020/01/20/2 | ||
| ALPINE_COMPILE_FLAGS: ${NIX_COMPILE_FLAGS} -Wno-overflow | ||
| steps: | ||
| - uses: actions/checkout@v3 | ||
| with: | ||
| path: hidapisrc | ||
| - name: Install dependencies | ||
| run: | | ||
| apk add gcc musl-dev autoconf automake libtool eudev-dev libusb-dev linux-headers cmake ninja make | ||
| - name: Configure CMake | ||
| run: | | ||
| rm -rf build install | ||
| cmake -B build/shared-cmake -S hidapisrc -GNinja -DCMAKE_BUILD_TYPE=RelWithDebInfo -DCMAKE_INSTALL_PREFIX=install/shared-cmake -DHIDAPI_BUILD_HIDTEST=ON "-DCMAKE_C_FLAGS=${ALPINE_COMPILE_FLAGS}" | ||
| cmake -B build/static-cmake -S hidapisrc -GNinja -DCMAKE_BUILD_TYPE=RelWithDebInfo -DCMAKE_INSTALL_PREFIX=install/static-cmake -DBUILD_SHARED_LIBS=FALSE -DHIDAPI_BUILD_HIDTEST=ON "-DCMAKE_C_FLAGS=${ALPINE_COMPILE_FLAGS}" | ||
| - name: Configure Automake | ||
| working-directory: hidapisrc | ||
| run: | | ||
| ./bootstrap | ||
| ./configure | ||
| - name: Build CMake Shared | ||
| working-directory: build/shared-cmake | ||
| run: ninja install | ||
| - name: Build CMake Static | ||
| working-directory: build/static-cmake | ||
| run: ninja install | ||
| - name: Build Automake | ||
| working-directory: hidapisrc | ||
| run: | | ||
| make | ||
| make DESTDIR=$PWD/../install/automake install | ||
| make clean | ||
| - name: Build manual Makefile | ||
| run: | | ||
| cd hidapisrc/linux | ||
| make -f Makefile-manual | ||
| cd ../libusb | ||
| make -f Makefile-manual | ||
| - name: Check artifacts | ||
| uses: andstor/file-existence-action@v2 | ||
| with: | ||
| files: "install/shared-cmake/lib/libhidapi-libusb.so, \ | ||
| install/shared-cmake/lib/libhidapi-hidraw.so, \ | ||
| install/shared-cmake/include/hidapi/hidapi.h, \ | ||
| install/shared-cmake/include/hidapi/hidapi_libusb.h, \ | ||
| install/static-cmake/lib/libhidapi-libusb.a, \ | ||
| install/static-cmake/lib/libhidapi-hidraw.a, \ | ||
| install/static-cmake/include/hidapi/hidapi.h, \ | ||
| install/static-cmake/include/hidapi/hidapi_libusb.h" | ||
| fail: true | ||
| - name: Check CMake Export Package Shared | ||
| run: | | ||
| cmake \ | ||
| -GNinja \ | ||
| -B build/shared_test \ | ||
| -S hidapisrc/hidtest \ | ||
| -Dhidapi_ROOT=install/shared-cmake \ | ||
| -DCMAKE_INSTALL_PREFIX=install/shared_test \ | ||
| "-DCMAKE_C_FLAGS=${ALPINE_COMPILE_FLAGS}" | ||
| cd build/shared_test | ||
| ninja install | ||
| - name: Check CMake Export Package Static | ||
| run: | | ||
| cmake \ | ||
| -GNinja \ | ||
| -B build/static_test \ | ||
| -S hidapisrc/hidtest \ | ||
| -Dhidapi_ROOT=install/static-cmake \ | ||
| -DCMAKE_INSTALL_PREFIX=install/static_test \ | ||
| "-DCMAKE_C_FLAGS=${ALPINE_COMPILE_FLAGS}" | ||
| cd build/static_test | ||
| ninja install |
| name: Checks | ||
| run-name: Code checks for '${{ github.ref_name }}' | ||
| # General comment: | ||
| # Coverity doesn't support merging or including reports from multible machine/platforms (at least not officially). | ||
| # But otherwise there is no good way to keep the issues from all platforms at Coverity Scans at once. | ||
| # This script uses undocumented (but appears to be working) hack: | ||
| # The build logs from one machine/platform gets moved to a next once, | ||
| # and "fixed" so that cov-build can append logs from the next platform. | ||
| # The "fix" is based on the fact, that Coverity perfectly allows appending logs from multiple builds | ||
| # that are done *on the same host* machine. | ||
| on: | ||
| # On-demand run | ||
| workflow_dispatch: | ||
| # Weekly run | ||
| schedule: | ||
| - cron: '30 5 * * 0' | ||
| jobs: | ||
| coverity-windows: | ||
| runs-on: windows-latest | ||
| steps: | ||
| - uses: actions/checkout@v3 | ||
| with: | ||
| path: src | ||
| - name: Setup MSVC | ||
| uses: TheMrMilchmann/setup-msvc-dev@v2.0.0 | ||
| with: | ||
| arch: x64 | ||
| - name: Configure | ||
| run: | | ||
| cmake -B build -S src -G"NMake Makefiles" -DCMAKE_BUILD_TYPE=RelWithDebInfo -DHIDAPI_WITH_TESTS=ON -DHIDAPI_BUILD_HIDTEST=ON | ||
| - name: Lookup Coverity Build Tool hash | ||
| id: coverity-cache-lookup | ||
| run: | | ||
| $coverity_hash=Invoke-RestMethod -Uri https://scan.coverity.com/download/cxx/win64 -Method Post -Body @{token='${{ secrets.COVERITY_SCAN_TOKEN }}';project='hidapi';md5=1} | ||
| echo "coverity_hash=$coverity_hash" >> $Env:GITHUB_OUTPUT | ||
| - name: Get cached Coverity Build Tool | ||
| id: cov-build-cache | ||
| uses: actions/cache@v3 | ||
| with: | ||
| path: cov-root | ||
| key: cov-root-cxx-win64-${{ steps.coverity-cache-lookup.outputs.coverity_hash }} | ||
| - name: Get and configure Coverity | ||
| if: steps.cov-build-cache.outputs.cache-hit != 'true' | ||
| run: | | ||
| Invoke-WebRequest -Uri https://scan.coverity.com/download/cxx/win64 -OutFile coverity.zip -Method Post -Body @{token='${{ secrets.COVERITY_SCAN_TOKEN }}';project='hidapi'} | ||
| Remove-Item 'cov-root' -Recurse -Force -ErrorAction SilentlyContinue | ||
| Expand-Archive coverity.zip -DestinationPath cov-root | ||
| $cov_root=Get-ChildItem -Path 'cov-root' | ||
| $Env:PATH += ";$($cov_root.FullName)\bin" | ||
| cov-configure -msvc | ||
| - name: Make Coverity available in PATH | ||
| run: | | ||
| $cov_root=Get-ChildItem -Path 'cov-root' | ||
| echo "$($cov_root.FullName)\bin" >> $Env:GITHUB_PATH | ||
| - name: Build with Coverity | ||
| working-directory: build | ||
| run: | | ||
| cov-build --dir cov-int nmake | ||
| Rename-Item ".\cov-int\emit\$(hostname)" hostname | ||
| - name: Backup Coverity logs | ||
| uses: actions/upload-artifact@v3 | ||
| with: | ||
| name: coverity-logs-windows | ||
| path: build/cov-int | ||
| retention-days: 7 | ||
| coverity-macos: | ||
| runs-on: macos-latest | ||
| needs: [coverity-windows] | ||
| steps: | ||
| - uses: actions/checkout@v3 | ||
| with: | ||
| path: src | ||
| - name: Install dependencies | ||
| run: brew install ninja | ||
| - name: Configure | ||
| run: | | ||
| cmake -B build -S src -GNinja -DCMAKE_BUILD_TYPE=RelWithDebInfo -DHIDAPI_WITH_TESTS=ON -DHIDAPI_BUILD_HIDTEST=ON -DCMAKE_C_COMPILER=clang | ||
| - uses: actions/download-artifact@v3 | ||
| with: | ||
| name: coverity-logs-windows | ||
| path: build/cov-int | ||
| - name: Fixup cov-int | ||
| run: | | ||
| rm -f build/cov-int/emit/hostname/emit-db.lock build/cov-int/emit/hostname/emit-db.write-lock | ||
| mv build/cov-int/emit/hostname build/cov-int/emit/$(hostname) | ||
| - name: Lookup Coverity Build Tool hash | ||
| id: coverity-cache-lookup | ||
| shell: bash | ||
| run: | | ||
| hash=$(curl https://scan.coverity.com/download/cxx/Darwin --data "token=${{ secrets.COVERITY_SCAN_TOKEN }}&project=hidapi&md5=1") | ||
| echo "coverity_hash=${hash}" >> $GITHUB_OUTPUT | ||
| - name: Get cached Coverity Build Tool | ||
| id: cov-build-cache | ||
| uses: actions/cache@v3 | ||
| with: | ||
| path: cov-root | ||
| key: cov-root-cxx-Darwin-${{ steps.coverity-cache-lookup.outputs.coverity_hash }} | ||
| - name: Get and configure Coverity | ||
| if: steps.cov-build-cache.outputs.cache-hit != 'true' | ||
| run: | | ||
| curl https://scan.coverity.com/download/cxx/Darwin --output coverity.dmg --data "token=${{ secrets.COVERITY_SCAN_TOKEN }}&project=hidapi" | ||
| hdiutil attach coverity.dmg -mountroot coverity | ||
| export COV_DIR_NAME=$(ls -1 --color=never coverity) | ||
| rm -rf cov-root | ||
| mkdir cov-root | ||
| cp ./coverity/${COV_DIR_NAME}/${COV_DIR_NAME}.sh cov-root/ | ||
| cd cov-root/ | ||
| ./${COV_DIR_NAME}.sh | ||
| ./bin/cov-configure --clang | ||
| - name: Make Coverity available in PATH | ||
| run: echo "$(pwd)/cov-root/bin" >> $GITHUB_PATH | ||
| - name: Build with Coverity | ||
| working-directory: build | ||
| run: | | ||
| cov-build --dir cov-int --append-log ninja | ||
| mv cov-int/emit/$(hostname) cov-int/emit/hostname | ||
| - name: Backup Coverity logs | ||
| uses: actions/upload-artifact@v3 | ||
| with: | ||
| name: coverity-logs-windows-macos | ||
| path: build/cov-int | ||
| retention-days: 7 | ||
| coverity-ubuntu: | ||
| runs-on: ubuntu-latest | ||
| needs: [coverity-macos] | ||
| steps: | ||
| - uses: actions/checkout@v3 | ||
| with: | ||
| path: src | ||
| - name: Install dependencies | ||
| run: sudo apt install libudev-dev libusb-1.0-0-dev ninja-build | ||
| - name: Configure | ||
| run: | | ||
| cmake -B build -S src -GNinja -DCMAKE_BUILD_TYPE=RelWithDebInfo -DHIDAPI_WITH_TESTS=ON -DHIDAPI_BUILD_HIDTEST=ON -DCMAKE_C_COMPILER=gcc | ||
| - uses: actions/download-artifact@v3 | ||
| with: | ||
| name: coverity-logs-windows-macos | ||
| path: build/cov-int | ||
| - name: Fixup cov-int | ||
| run: | | ||
| rm -f build/cov-int/emit/hostname/emit-db.lock build/cov-int/emit/hostname/emit-db.write-lock | ||
| mv build/cov-int/emit/hostname build/cov-int/emit/$(hostname) | ||
| - name: Lookup Coverity Build Tool hash | ||
| id: coverity-cache-lookup | ||
| shell: bash | ||
| run: | | ||
| hash=$(curl https://scan.coverity.com/download/cxx/linux64 --data "token=${{ secrets.COVERITY_SCAN_TOKEN }}&project=hidapi&md5=1") | ||
| echo "coverity_hash=${hash}" >> $GITHUB_OUTPUT | ||
| - name: Get cached Coverity Build Tool | ||
| id: cov-build-cache | ||
| uses: actions/cache@v3 | ||
| with: | ||
| path: cov-root | ||
| key: cov-root-cxx-linux64-${{ steps.coverity-cache-lookup.outputs.coverity_hash }} | ||
| - name: Get and configure Coverity | ||
| if: steps.cov-build-cache.outputs.cache-hit != 'true' | ||
| run: | | ||
| curl https://scan.coverity.com/download/cxx/linux64 --output coverity.tar.gz --data "token=${{ secrets.COVERITY_SCAN_TOKEN }}&project=hidapi" | ||
| rm -rf cov-root | ||
| mkdir cov-root | ||
| tar -xzf coverity.tar.gz --strip 1 -C cov-root | ||
| ./cov-root/bin/cov-configure --gcc | ||
| - name: Make Coverity available in PATH | ||
| run: echo "$(pwd)/cov-root/bin" >> $GITHUB_PATH | ||
| - name: Build with Coverity | ||
| working-directory: build | ||
| run: | | ||
| cov-build --dir cov-int --append-log ninja | ||
| - name: Submit results to Coverity Scan | ||
| working-directory: build | ||
| run: | | ||
| tar -czf cov-int.tar.gz cov-int | ||
| curl --form token=${{ secrets.COVERITY_SCAN_TOKEN }} \ | ||
| --form email=${{ secrets.COVERITY_SCAN_EMAIL }} \ | ||
| --form file=@cov-int.tar.gz \ | ||
| --form version="$GITHUB_SHA" \ | ||
| --form description="Automatic HIDAPI build" \ | ||
| https://scan.coverity.com/builds?project=hidapi | ||
| mv cov-int/emit/$(hostname) cov-int/emit/hostname | ||
| - name: Backup Coverity logs | ||
| uses: actions/upload-artifact@v3 | ||
| with: | ||
| name: coverity-logs-windows-macos-linux | ||
| path: build/cov-int | ||
| retention-days: 7 |
| name: Docs | ||
| on: | ||
| push: | ||
| branches: [master] | ||
| pull_request: | ||
| branches: [master] | ||
| jobs: | ||
| build: | ||
| runs-on: ubuntu-latest | ||
| steps: | ||
| - name: Install Doxygen static libclang deps | ||
| run: sudo apt-get install libclang1-12 libclang-cpp12 | ||
| - name: Install Doxygen from SF binary archives | ||
| env: | ||
| DOXYGEN_VERSION: '1.9.6' | ||
| run: | | ||
| mkdir .doxygen && cd .doxygen | ||
| curl -L https://sourceforge.net/projects/doxygen/files/rel-$DOXYGEN_VERSION/doxygen-$DOXYGEN_VERSION.linux.bin.tar.gz > doxygen.tar.gz | ||
| gunzip doxygen.tar.gz | ||
| tar xf doxygen.tar | ||
| cd doxygen-$DOXYGEN_VERSION | ||
| sudo make install | ||
| - uses: actions/checkout@v3 | ||
| - run: doxygen | ||
| working-directory: doxygen | ||
| - name: Save doxygen docs as artifact | ||
| uses: actions/upload-artifact@v3 | ||
| with: | ||
| name: HIDAPI_doxygen_docs | ||
| path: ${{ github.workspace }}/doxygen/html | ||
| deploy-docs: | ||
| runs-on: ubuntu-latest | ||
| needs: [build] | ||
| if: github.ref_type == 'branch' && github.ref_name == 'master' | ||
| concurrency: | ||
| group: "github-pages-deploy" | ||
| cancel-in-progress: true | ||
| steps: | ||
| - name: downlod artifact | ||
| uses: actions/download-artifact@v3 | ||
| with: | ||
| name: HIDAPI_doxygen_docs | ||
| path: docs | ||
| - name: upload to github pages | ||
| uses: peaceiris/actions-gh-pages@v3 | ||
| with: | ||
| github_token: ${{ secrets.GITHUB_TOKEN }} | ||
| publish_dir: ./docs | ||
| force_orphan: true |
| # Autotools-added generated files | ||
| Makefile.in | ||
| aclocal.m4 | ||
| ar-lib | ||
| autom4te.cache/ | ||
| config.* | ||
| configure | ||
| configure~ | ||
| compile | ||
| depcomp | ||
| install-sh | ||
| libusb/Makefile.in | ||
| linux/Makefile.in | ||
| ltmain.sh | ||
| mac/Makefile.in | ||
| missing | ||
| testgui/Makefile.in | ||
| windows/Makefile.in | ||
| Makefile | ||
| stamp-h1 | ||
| libtool | ||
| # macOS | ||
| .DS_Store | ||
| # Qt Creator | ||
| CMakeLists.txt.user | ||
| # doxgen output | ||
| doxygen/html/ |
| LOCAL_PATH:= $(call my-dir) | ||
| HIDAPI_ROOT_REL:= ../.. | ||
| HIDAPI_ROOT_ABS:= $(LOCAL_PATH)/../.. | ||
| include $(CLEAR_VARS) | ||
| LOCAL_SRC_FILES := \ | ||
| $(HIDAPI_ROOT_REL)/libusb/hid.c | ||
| LOCAL_C_INCLUDES += \ | ||
| $(HIDAPI_ROOT_ABS)/hidapi \ | ||
| $(HIDAPI_ROOT_ABS)/android | ||
| LOCAL_SHARED_LIBRARIES := libusb1.0 | ||
| LOCAL_MODULE := libhidapi | ||
| include $(BUILD_SHARED_LIBRARY) |
| HIDAPI Authors: | ||
| Alan Ott <alan@signal11.us>: | ||
| Original Author and Maintainer | ||
| Linux, Windows, and Mac implementations | ||
| Ludovic Rousseau <rousseau@debian.org>: | ||
| Formatting for Doxygen documentation | ||
| Bug fixes | ||
| Correctness fixes | ||
| libusb/hidapi Team: | ||
| Development/maintenance since June 4th 2019 | ||
| For a comprehensive list of contributions, see the commit list at github: | ||
| https://github.com/libusb/hidapi/graphs/contributors | ||
Sorry, the diff of this file is not supported yet
| # Building HIDAPI using Autotools (deprecated) | ||
| --- | ||
| **NOTE**: for all intentions and purposes the Autotools build scripts for HIDAPI are _deprecated_ and going to be obsolete in the future. | ||
| HIDAPI Team recommends using CMake build for HIDAPI. | ||
| If you are already using Autotools build scripts provided by HIDAPI, | ||
| consider switching to CMake build scripts as soon as possible. | ||
| --- | ||
| To be able to use Autotools to build HIDAPI, it has to be [installed](#installing-autotools)/available in the system. | ||
| Make sure you've checked [prerequisites](BUILD.md#prerequisites) and installed all required dependencies. | ||
| ## Installing Autotools | ||
| HIDAPI uses few specific tools/packages from Autotools: `autoconf`, `automake`, `libtool`. | ||
| On different platforms or package managers, those could be named a bit differently or packaged together. | ||
| You'll have to check the documentation/package list for your specific package manager. | ||
| ### Linux | ||
| On Ubuntu the tools are available via APT: | ||
| ```sh | ||
| sudo apt install autoconf automake libtool | ||
| ``` | ||
| ### FreeBSD | ||
| FreeBSD Autotools can be installed as: | ||
| ```sh | ||
| pkg_add -r autotools | ||
| ``` | ||
| Additionally, on FreeBSD you will need to install GNU make: | ||
| ```sh | ||
| pkg_add -r gmake | ||
| ``` | ||
| ## Building HIDAPI with Autotools | ||
| A simple command list, to build HIDAPI with Autotools as a _shared library_ and install in into your system: | ||
| ```sh | ||
| ./bootstrap # this prepares the configure script | ||
| ./configure | ||
| make # build the library | ||
| make install # as root, or using sudo, this will install hidapi into your system | ||
| ``` | ||
| `./configure` can take several arguments which control the build. A few commonly used options: | ||
| ```sh | ||
| --enable-testgui | ||
| # Enable the build of Foxit-based Test GUI. This requires Fox toolkit to | ||
| # be installed/available. See README.md#test-gui for remarks. | ||
| --prefix=/usr | ||
| # Specify where you want the output headers and libraries to | ||
| # be installed. The example above will put the headers in | ||
| # /usr/include and the binaries in /usr/lib. The default is to | ||
| # install into /usr/local which is fine on most systems. | ||
| --disable-shared | ||
| # By default, both shared and static libraries are going to be built/installed. | ||
| # This option disables shared library build, if only static library is required. | ||
| ``` | ||
| ## Cross Compiling | ||
| This section talks about cross compiling HIDAPI for Linux using Autotools. | ||
| This is useful for using HIDAPI on embedded Linux targets. These | ||
| instructions assume the most raw kind of embedded Linux build, where all | ||
| prerequisites will need to be built first. This process will of course vary | ||
| based on your embedded Linux build system if you are using one, such as | ||
| OpenEmbedded or Buildroot. | ||
| For the purpose of this section, it will be assumed that the following | ||
| environment variables are exported. | ||
| ```sh | ||
| $ export STAGING=$HOME/out | ||
| $ export HOST=arm-linux | ||
| ``` | ||
| `STAGING` and `HOST` can be modified to suit your setup. | ||
| ### Prerequisites | ||
| Depending on what backend you want to cross-compile, you also need to prepare the dependencies: | ||
| `libusb` for libusb HIDAPI backend, or `libudev` for hidraw HIDAPI backend. | ||
| An example of cross-compiling `libusb`. From `libusb` source directory, run: | ||
| ```sh | ||
| ./configure --host=$HOST --prefix=$STAGING | ||
| make | ||
| make install | ||
| ``` | ||
| An example of cross-comping `libudev` is not covered by this section. | ||
| Check `libudev`'s documentation for details. | ||
| ### Building HIDAPI | ||
| Build HIDAPI: | ||
| ```sh | ||
| PKG_CONFIG_DIR= \ | ||
| PKG_CONFIG_LIBDIR=$STAGING/lib/pkgconfig:$STAGING/share/pkgconfig \ | ||
| PKG_CONFIG_SYSROOT_DIR=$STAGING \ | ||
| ./configure --host=$HOST --prefix=$STAGING | ||
| # make / make install - same as for a regular build | ||
| ``` |
| # Building HIDAPI using CMake | ||
| To build HIDAPI with CMake, it has to be [installed](#installing-cmake)/available in the system. | ||
| Make sure you've checked [prerequisites](BUILD.md#prerequisites) and installed all required dependencies. | ||
| HIDAPI CMake build system allows you to build HIDAPI in two generally different ways: | ||
| 1) As a [standalone package/library](#standalone-package-build); | ||
| 2) As [part of a larger CMake project](#hidapi-as-a-subdirectory). | ||
| **TL;DR**: if you're experienced developer and have been working with CMake projects or have been written some of your own - | ||
| most of this document may not be of interest for you; just check variables names, its default values and the target names. | ||
| ## Installing CMake | ||
| CMake can be installed either using your system's package manager, | ||
| or by downloading an installer/prebuilt version from the [official website](https://cmake.org/download/). | ||
| On most \*nix systems, the preferred way to install CMake is via package manager, | ||
| e.g. `sudo apt install cmake`. | ||
| On Windows CMake could be provided by your development environment (e.g. by Visual Studio Installer or MinGW installer), | ||
| or you may install it system-wise using the installer from the official website. | ||
| On macOS CMake may be installed by Homebrew/MacPorts or using the installer from the official website. | ||
| ## Standalone package build | ||
| To build HIDAPI as a standalone package, you follow [general steps](https://cmake.org/runningcmake/) of building any CMake project. | ||
| An example of building HIDAPI with CMake: | ||
| ```sh | ||
| # precondition: create a <build dir> somewhere on the filesystem (preferably outside of the HIDAPI source) | ||
| # this is the place where all intermediate/build files are going to be located | ||
| cd <build dir> | ||
| # configure the build | ||
| cmake <HIDAPI source dir> | ||
| # build it! | ||
| cmake --build . | ||
| # install library; by default installs into /usr/local/ | ||
| cmake --build . --target install | ||
| # NOTE: you need to run install command as root, to be able to install into /usr/local/ | ||
| ``` | ||
| Such invocation will use the default (as per CMake magic) compiler/build environment available in your system. | ||
| You may pass some additional CMake variables to control the build configuration as `-D<CMake Variable>=value`. | ||
| E.g.: | ||
| ```sh | ||
| # install command now would install things into /usr | ||
| cmake <HIDAPI source dir> -DCMAKE_INSTALL_PREFIX=/usr | ||
| ``` | ||
| <details> | ||
| <summary>Using a specific CMake generator</summary> | ||
| An example of using `Ninja` as a CMake generator: | ||
| ```sh | ||
| cd <build dir> | ||
| # configure the build | ||
| cmake -GNinja <HIDAPI source dir> | ||
| # we know, that CMake has generated build files for Ninja, | ||
| # so we can use `ninja` directly, instead of `cmake --build .` | ||
| ninja | ||
| # install library | ||
| ninja install | ||
| ``` | ||
| `-G` here specifies a native build system CMake would generate build files for. | ||
| Check [CMake Documentation](https://cmake.org/cmake/help/latest/manual/cmake-generators.7.html) for a list of available generators (system-specific). | ||
| </details><br> | ||
| Some of the [standard](https://cmake.org/cmake/help/latest/manual/cmake-variables.7.html) CMake variables you may want to use to configure a build: | ||
| - [`CMAKE_INSTALL_PREFIX`](https://cmake.org/cmake/help/latest/variable/CMAKE_INSTALL_PREFIX.html) - prefix where `install` target would install the library(ies); | ||
| - [`CMAKE_BUILD_TYPE`](https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html) - standard possible values: `Debug`, `Release`, `RelWithDebInfo`, `MinSizeRel`; Defaults to `Release` for HIDAPI, if not specified; | ||
| - [`BUILD_SHARED_LIBS`](https://cmake.org/cmake/help/latest/variable/BUILD_SHARED_LIBS.html) - when set to TRUE, HIDAPI is built as a shared library, otherwise build statically; Defaults to `TRUE` for HIDAPI, if not specified; | ||
| <details> | ||
| <summary>macOS-specific variables</summary> | ||
| - [`CMAKE_FRAMEWORK`](https://cmake.org/cmake/help/latest/variable/CMAKE_FRAMEWORK.html) - (since CMake 3.15) when set to TRUE, HIDAPI is built as a framework library, otherwise build as a regular static/shared library; Defaults to `FALSE` for HIDAPI, if not specified; | ||
| - [`CMAKE_OSX_DEPLOYMENT_TARGET`](https://cmake.org/cmake/help/latest/variable/CMAKE_OSX_DEPLOYMENT_TARGET.html) - minimum version of the target platform (e.g. macOS or iOS) on which the target binaries are to be deployed; defaults to a maximum supported target platform by currently used XCode/Toolchain; | ||
| </details><br> | ||
| HIDAPI-specific CMake variables: | ||
| - `HIDAPI_BUILD_HIDTEST` - when set to TRUE, build a small test application `hidtest`; | ||
| - `HIDAPI_WITH_TESTS` - when set to TRUE, build all (unit-)tests; | ||
| currently this option is only available on Windows, since only Windows backend has tests; | ||
| <details> | ||
| <summary>Linux-specific variables</summary> | ||
| - `HIDAPI_WITH_HIDRAW` - when set to TRUE, build HIDRAW-based implementation of HIDAPI (`hidapi-hidraw`), otherwise don't build it; defaults to TRUE; | ||
| - `HIDAPI_WITH_LIBUSB` - when set to TRUE, build LIBUSB-based implementation of HIDAPI (`hidapi-libusb`), otherwise don't build it; defaults to TRUE; | ||
| **NOTE**: at least one of `HIDAPI_WITH_HIDRAW` or `HIDAPI_WITH_LIBUSB` has to be set to TRUE. | ||
| </details><br> | ||
| To see all most-useful CMake variables available for HIDAPI, one of the most convenient ways is too use [`cmake-gui`](https://cmake.org/cmake/help/latest/manual/cmake-gui.1.html) tool ([example](https://cmake.org/runningcmake/)). | ||
| _NOTE_: HIDAPI packages built by CMake can be used with `pkg-config`, as if built with [Autotools](BUILD.autotools.md). | ||
| ### MSVC and Ninja | ||
| It is possible to build a CMake project (including HIDAPI) using MSVC compiler and Ninja (for medium and larger projects it is so much faster than msbuild). | ||
| For that: | ||
| 1) Open cmd.exe; | ||
| 2) Setup MSVC build environment variables, e.g.: `vcvarsall.bat x64`, where: | ||
| - `vcvarsall.bat` is an environment setup script of your MSVC toolchain installation;<br>For MSVC 2019 Community edition it is located at: `C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Auxiliary\Build\`; | ||
| - `x64` -a target architecture to build; | ||
| 3) Follow general build steps, and use `Ninja` as a generator. | ||
| ### Using HIDAPI in a CMake project | ||
| When HIDAPI is used as a standalone package (either installed into the system or built manually and installed elsewhere), the simplest way to use it is as showed in the example: | ||
| ```cmake | ||
| project(my_application) | ||
| add_executable(my_application main.c) | ||
| find_package(hidapi REQUIRED) | ||
| target_link_libraries(my_application PRIVATE hidapi::hidapi) | ||
| ``` | ||
| If HIDAPI isn't installed in your system, or `find_package` cannot find HIDAPI by default for any other reasons, | ||
| the recommended way manually specify which HIDAPI package to use is via `hidapi_ROOT` CMake variable, e.g.: | ||
| `-Dhidapi_ROOT=<path to HIDAPI installation prefix>`. | ||
| _NOTE_: usage of `hidapi_ROOT` is only possible (and recommended) with CMake 3.12 and higher. For older versions of CMake you'd need to specify [`CMAKE_PREFIX_PATH`](https://cmake.org/cmake/help/latest/variable/CMAKE_PREFIX_PATH.html#variable:CMAKE_PREFIX_PATH) instead. | ||
| Check with [`find_package`](https://cmake.org/cmake/help/latest/command/find_package.html) documentation if you need more details. | ||
| Available CMake targets after successful `find_package(hidapi)`: | ||
| - `hidapi::hidapi` - indented to be used in most cases; | ||
| - `hidapi::include` - if you need only to include `<hidapi.h>` but not link against the library; | ||
| - `hidapi::winapi` - same as `hidapi::hidapi` on Windows; available only on Windows; | ||
| - `hidapi::darwin` - same as `hidapi::hidapi` on macOS; available only on macOS; | ||
| - `hidapi::libusb` - available when libusb backend is used/available; | ||
| - `hidapi::hidraw` - available when hidraw backend is used/available on Linux; | ||
| **NOTE**: on Linux often both `hidapi::libusb` and `hidapi::hidraw` backends are available; in that case `hidapi::hidapi` is an alias for **`hidapi::hidraw`**. The motivation is that `hidraw` backend is a native Linux kernel implementation of HID protocol, and supports various HID devices (USB, Bluetooth, I2C, etc.). If `hidraw` backend isn't built at all (`hidapi::libusb` is the only target) - `hidapi::hidapi` is an alias for `hidapi::libusb`. | ||
| If you're developing a cross-platform application and you are sure you need to use `libusb` backend on Linux, the simple way to achieve this is: | ||
| ```cmake | ||
| if(TARGET hidapi::libusb) | ||
| target_link_libraries(my_project PRIVATE hidapi::libusb) | ||
| else() | ||
| target_link_libraries(my_project PRIVATE hidapi::hidapi) | ||
| endif() | ||
| ``` | ||
| ## HIDAPI as a subdirectory | ||
| HIDAPI can be easily used as a subdirectory of a larger CMake project: | ||
| ```cmake | ||
| # root CMakeLists.txt | ||
| cmake_minimum_required(VERSION 3.4.3 FATAL_ERROR) | ||
| add_subdirectory(hidapi) | ||
| add_subdirectory(my_application) | ||
| # my_application/CMakeLists.txt | ||
| project(my_application) | ||
| add_executable(my_application main.c) | ||
| # NOTE: no `find_package` is required, since HIDAPI targets are already a part of the project tree | ||
| target_link_libraries(my_application PRIVATE hidapi::hidapi) | ||
| ``` | ||
| Lets call this "larger project" a "host project". | ||
| All of the variables described in [standalone build](#standalone-package-build) section can be used to control HIDAPI build in case of a subdirectory, e.g.: | ||
| ```cmake | ||
| set(HIDAPI_WITH_LIBUSB FALSE) # surely will be used only on Linux | ||
| set(BUILD_SHARED_LIBS FALSE) # HIDAPI as static library on all platforms | ||
| add_subdirectory(hidapi) | ||
| ``` | ||
| <details> | ||
| <summary>NOTE</summary> | ||
| If you project happen to use `BUILD_SHARED_LIBS` as a `CACHE` variable globally for you project, setting it as simple variable, as showed above _will have not affect_ up until _CMake 3.13_. See [CMP0077](https://cmake.org/cmake/help/latest/policy/CMP0077.html) for details. | ||
| </details><br> | ||
| There are several important differences in the behavior of HIDAPI CMake build system when CMake is built as standalone package vs subdirectory build: | ||
| 1) In _standalone build_ a number of standard and HIDAPI-specific variables are marked as _cache variables_ or _options_. | ||
| This is done for convenience: when you're building HIDAPI as a standalone package and using tools like `cmake-gui` - those are highlighted as variables that can be changed and has some short description/documentation. E.g.: | ||
| <br> | ||
| E.g.2:<br> | ||
| <br> | ||
| When HIDAPI is built as a _subdirectory_ - **_none of the variables are marked for cache or as options_** by HIDAPI. | ||
| This is done to let the host project's developer decide what is important (what needs to be highlighted) and what's not. | ||
| 2) The default behavior/default value for some of the variables is a bit different: | ||
| - by default, none of HIDAPI targets are [installed](https://cmake.org/cmake/help/latest/command/install.html); if required, HIDAPI targets can be installed by host project _after_ including HIDAPI subdirectory (requires CMake 3.13 or later); **or**, the default installation can be enabled by setting `HIDAPI_INSTALL_TARGETS` variable _before_ including HIDAPI subdirectory. | ||
| HIDAPI uses [GNUInstallDirs](https://cmake.org/cmake/help/latest/module/GNUInstallDirs.html) to specify install locations. Variables like `CMAKE_INSTALL_LIBDIR` can be used to control HIDAPI's installation locations. E.g.: | ||
| ```cmake | ||
| # enable the installation if you need it | ||
| set(HIDAPI_INSTALL_TARGETS ON) | ||
| # (optionally) change default installation locations if it makes sense for your target platform, etc. | ||
| set(CMAKE_INSTALL_LIBDIR "lib64") | ||
| add_subdirectory(hidapi) | ||
| ``` | ||
| - HIDAPI prints its version during the configuration when built as a standalone package; to enable this for subdirectory builds - set `HIDAPI_PRINT_VERSION` to TRUE before including HIDAPI; | ||
| 3) In a subdirectory build, HIDAPI _doesn't modify or set any of the CMake variables_ that may change the build behavior. | ||
| For instance, in a _standalone build_, if CMAKE_BUILD_TYPE or BUILD_SHARED_LIBS variables are not set, those are defaulted to "Release" and "TRUE" explicitly. | ||
| In a _subdirectory build_, even if not set, those variables remain unchanged, so a host project's developer has a full control over the HIDAPI build configuration. | ||
| Available CMake targets after `add_subdirectory(hidapi)` _are the same as in case of [standalone build](#standalone-package-build)_, and a few additional ones: | ||
| - `hidapi_include` - the interface library; `hidapi::hidapi` is an alias of it; | ||
| - `hidapi_winapi` - library target on Windows; `hidapi::winapi` is an alias of it; | ||
| - `hidapi_darwin` - library target on macOS; `hidapi::darwin` is an alias of it; | ||
| - `hidapi_libusb` - library target for libusb backend; `hidapi::libusb` is an alias of it; | ||
| - `hidapi_hidraw` - library target for hidraw backend; `hidapi::hidraw` is an alias of it; | ||
| - `hidapi-libusb` - an alias of `hidapi_libusb` for compatibility with raw library name; | ||
| - `hidapi-hidraw` - an alias of `hidapi_hidraw` for compatibility with raw library name; | ||
| - `hidapi` - an alias of `hidapi_winapi` or `hidapi_darwin` on Windows or macOS respectfully. | ||
| Advanced: | ||
| - Why would I need additional targets described in this section above, if I already have alias targets compatible with `find_package`? | ||
| - an example: | ||
| ```cmake | ||
| add_subdirectory(hidapi) | ||
| if(TARGET hidapi_libusb) | ||
| # see libusb/hid.c for usage of `NO_ICONV` | ||
| target_compile_definitions(hidapi_libusb PRIVATE NO_ICONV) | ||
| endif() | ||
| ``` | ||
| ## Both Shared and Static build | ||
| If you're a former (or present) user of Autotools build scripts for HIDAPI, or you're a package manager maintainer and you're often working with those - you're likely asking how to build HIDAPI with CMake and get both Shared and Static libraries (as would be done by Autotools: `./configure --enable-static --enable-shared ...`). | ||
| CMake doesn't have such option of-the-box and it is decided not to introduce any manual CMake-level workarounds for HIDAPI on this matter. | ||
| If you want to mimic the Autotools behavior, it is possible by building/installing first the static version of the library and then shared version of the library. The installation folder (`CMAKE_INSTALL_PREFIX`) should point to the same directory for both variants, that way: | ||
| - both static and shared library binaries will be available and usable; | ||
| - a single header file(s) for both of them; | ||
| - Autotools/pkg-config (`.pc`) files will be generated and usable _as if_ generated by Autotools natively and build configured with both `-enable-static --enable-shared` options; | ||
| - CMake package scripts will be generated and fully usable, but _only the last build installed_, i.e. if the last was installed Shared version of the binary - CMake targets found by `find_package(hidapi)` would point to a Shared binaries. | ||
| There is a historical discussion, why such solution is simplest/preferable: https://github.com/libusb/hidapi/issues/424 | ||
| #### TL;DR/Sample | ||
| ```sh | ||
| # First - configure/build | ||
| # Static libraries | ||
| cmake -S <HIDAPI source dir> -B "<build dir>/static" -DCMAKE_INSTALL_PREFIX=<your installation prefix> -DBUILD_SHARED_LIBS=FALSE | ||
| cmake --build "<build dir>/static" | ||
| # Shared libraries | ||
| cmake -S <HIDAPI source dir> -B "<build dir>/shared" -DCMAKE_INSTALL_PREFIX=<your installation prefix> -DBUILD_SHARED_LIBS=TRUE | ||
| cmake --build "<build dir>/shared" | ||
| # (Optionally) change the installation destination. | ||
| # NOTE1: this is supported by CMake only on UNIX platforms | ||
| # See https://cmake.org/cmake/help/latest/envvar/DESTDIR.html | ||
| # NOTE2: this is not the same as `CMAKE_INSTALL_PREFIX` set above | ||
| # NOTE3: this is only required if you have a staging dir other than the final runtime dir, | ||
| # e.g. during cross-compilation | ||
| export DESTDIR="$STAGING_DIR" | ||
| # | ||
| # Install the libraries | ||
| # NOTE: order of installation matters - install Shared variant *the last* | ||
| # Static libraries | ||
| cmake --install "<build dir>/static" | ||
| # Shared libraries | ||
| cmake --install "<build dir>/shared" | ||
| ``` |
| # Building HIDAPI from Source | ||
| ## Table of content | ||
| * [Intro](#intro) | ||
| * [Prerequisites](#prerequisites) | ||
| * [Linux](#linux) | ||
| * [FreeBSD](#freebsd) | ||
| * [Mac](#mac) | ||
| * [Windows](#windows) | ||
| * [Embedding HIDAPI directly into your source tree](#embedding-hidapi-directly-into-your-source-tree) | ||
| * [Building the manual way on Unix platforms](#building-the-manual-way-on-unix-platforms) | ||
| * [Building on Windows](#building-on-windows) | ||
| ## Intro | ||
| For various reasons, you may need to build HIDAPI on your own. | ||
| It can be done in several different ways: | ||
| - using [CMake](BUILD.cmake.md); | ||
| - using [Autotools](BUILD.autotools.md) (deprecated); | ||
| - using [manual makefiles](#building-the-manual-way-on-unix-platforms); | ||
| - using `Meson` (requires CMake); | ||
| **Autotools** build system is historically the first mature build system for | ||
| HIDAPI. The most common usage of it is in its separate README: [BUILD.autotools.md](BUILD.autotools.md).<br/> | ||
| NOTE: for all intentions and purposes the Autotools build scripts for HIDAPI are _deprecated_ and going to be obsolete in the future. | ||
| HIDAPI Team recommends using CMake build for HIDAPI. | ||
| **CMake** build system is de facto an industry standard for many open-source and proprietary projects and solutions. | ||
| HIDAPI is one of the projects which use the power of CMake to its advantage. | ||
| More documentation is available in its separate README: [BUILD.cmake.md](BUILD.cmake.md). | ||
| **Meson** build system for HIDAPI is designed as a [wrapper](https://mesonbuild.com/CMake-module.html) over CMake build script. | ||
| It is present for the convenience of Meson users who need to use HIDAPI and need to be sure HIDAPI is built in accordance with officially supported build scripts.<br> | ||
| In the Meson script of your project you need a `hidapi = subproject('hidapi')` subproject, and `hidapi.get_variable('hidapi_dep')` as your dependency. | ||
| There are also backend/platform-specific dependencies available: `hidapi_winapi`, `hidapi_darwin`, `hidapi_hidraw`, `hidapi_libusb`. | ||
| If you don't know where to start to build HIDAPI, we recommend starting with [CMake](BUILD.cmake.md) build. | ||
| ## Prerequisites: | ||
| Regardless of what build system you choose to use, there are specific dependencies for each platform/backend. | ||
| ### Linux: | ||
| Depending on which backend you're going to build, you'll need to install | ||
| additional development packages. For `linux/hidraw` backend, you need a | ||
| development package for `libudev`. For `libusb` backend, naturally, you need | ||
| `libusb` development package. | ||
| On Debian/Ubuntu systems these can be installed by running: | ||
| ```sh | ||
| # required only by hidraw backend | ||
| sudo apt install libudev-dev | ||
| # required only by libusb backend | ||
| sudo apt install libusb-1.0-0-dev | ||
| ``` | ||
| ### FreeBSD: | ||
| On FreeBSD, you will need to install libiconv. This is done by running | ||
| the following: | ||
| ```sh | ||
| pkg_add -r libiconv | ||
| ``` | ||
| ### Mac: | ||
| Make sure you have XCode installed and its Command Line Tools. | ||
| ### Windows: | ||
| You just need a compiler. You may use Visual Studio or Cygwin/MinGW, | ||
| depending on which environment is best for your needs. | ||
| ## Embedding HIDAPI directly into your source tree | ||
| Instead of using one of the provided standalone build systems, | ||
| you may want to integrate HIDAPI directly into your source tree. | ||
| --- | ||
| If your project uses CMake as a build system, it is safe to add HIDAPI as a [subdirectory](BUILD.cmake.md#hidapi-as-a-subdirectory). | ||
| --- | ||
| If _the only option_ that works for you is adding HIDAPI sources directly | ||
| to your project's build system, then you need: | ||
| - include a _single source file_ into your project's build system, | ||
| depending on your platform and the backend you want to use: | ||
| - [`windows\hid.c`](windows/hid.c); | ||
| - [`linux/hid.c`](linux/hid.c); | ||
| - [`libusb/hid.c`](libusb/hid.c); | ||
| - [`mac/hid.c`](mac/hid.c); | ||
| - add a [`hidapi`](hidapi) folder to the include path when building `hid.c`; | ||
| - make the platform/backend specific [dependencies](#prerequisites) available during the compilation/linking, when building `hid.c`; | ||
| NOTE: the above doesn't guarantee that having a copy of `<backend>/hid.c` and `hidapi/hidapi.h` is enough to build HIDAPI. | ||
| The only guarantee that `<backend>/hid.c` includes all necessary sources to compile it as a single file. | ||
| Check the manual makefiles for a simple example/reference of what are the dependencies of each specific backend. | ||
| ## Building the manual way on Unix platforms | ||
| Manual Makefiles are provided mostly to give the user an idea what it takes | ||
| to build a program which embeds HIDAPI directly inside of it. These should | ||
| really be used as examples only. If you want to build a system-wide shared | ||
| library, use one of the build systems mentioned above. | ||
| To build HIDAPI using the manual Makefiles, change the directory | ||
| of your platform and run make. For example, on Linux run: | ||
| ```sh | ||
| cd linux/ | ||
| make -f Makefile-manual | ||
| ``` | ||
| ## Building on Windows | ||
| To build the HIDAPI DLL on Windows using Visual Studio, build the `.sln` file | ||
| in the `windows/` directory. | ||
| To build HIDAPI using MinGW or Cygwin using Autotools, use general Autotools | ||
| [instruction](BUILD.autotools.md). | ||
| Any windows builds (MSVC or MinGW/Cygwin) are also supported by [CMake](BUILD.cmake.md). | ||
| If you are looking for information regarding DDK build of HIDAPI: | ||
| - the build has been broken for a while and now the support files are obsolete. |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
| # HIDAPI Doxygen output | ||
| This site is dedicated to hosting an [API reference for the HIDAPI library](#API). | ||
| For general information, see the [source repository](https://github.com/libusb/hidapi#readme). | ||
| There are also build instructions hosted on github: | ||
| - [Building from source](https://github.com/libusb/hidapi/blob/master/BUILD.md) | ||
| - [Using CMake](https://github.com/libusb/hidapi/blob/master/BUILD.cmake.md) | ||
| - [Using Autotools (deprecated)](https://github.com/libusb/hidapi/blob/master/BUILD.autotools.md) | ||
| \example test.c contains a basic example usage of the HIDAPI library. |
| This file is mostly for the maintainer. | ||
| Updating a Version: | ||
| 1. Update VERSION file. | ||
| 2. HID_API_VERSION_MAJOR/HID_API_VERSION_MINOR/HID_API_VERSION_PATCH in hidapi.h. | ||
| Before firing a new release: | ||
| 1. Run the "Checks" Githtub Action | ||
| 2. Make sure no defects are found at: https://scan.coverity.com/projects/hidapi | ||
| 3. Fix if any | ||
| Firing a new release: | ||
| 1. Update the Version (if not yet updated). | ||
| 2. Prepare the Release Notes. | ||
| 3. Store the Release Notes into a file. | ||
| 4. Create locally an annotated git tag with release notes attached, e.g.: `git tag -aF ../hidapi_release_notes hidapi-<VERSION>` | ||
| 5. Push newly created tag: `git push origin hidapi-<VERSION>` | ||
| 6. Grab the hidapi-win.zip from Summary page of "GitHub Builds" Action for latest master build. | ||
| 7. Create a Github Release with hidapi-win.zip attached, for newly created tag. |
| Debug | ||
| Release | ||
| *.exp | ||
| *.ilk | ||
| *.lib | ||
| *.suo | ||
| *.vcproj.* | ||
| *.ncb | ||
| *.suo | ||
| *.dll | ||
| *.pdb | ||
| *.o | ||
| .deps/ | ||
| .libs/ | ||
| hidtest-hidraw | ||
| hidtest-libusb | ||
| hidtest |
| cmake_minimum_required(VERSION 3.1.3 FATAL_ERROR) | ||
| project(hidtest C) | ||
| if(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR) | ||
| # hidtest is build as a standalone project | ||
| if(POLICY CMP0074) | ||
| # allow using hidapi_ROOT if CMake supports it | ||
| cmake_policy(SET CMP0074 NEW) | ||
| endif() | ||
| find_package(hidapi 0.12 REQUIRED) | ||
| message(STATUS "Using HIDAPI: ${hidapi_VERSION}") | ||
| else() | ||
| # hidtest is built as part of the main HIDAPI build | ||
| message(STATUS "Building hidtest") | ||
| endif() | ||
| set(HIDAPI_HIDTEST_TARGETS) | ||
| if(NOT WIN32 AND NOT APPLE AND CMAKE_SYSTEM_NAME MATCHES "Linux") | ||
| if(TARGET hidapi::hidraw) | ||
| add_executable(hidtest_hidraw test.c) | ||
| target_link_libraries(hidtest_hidraw hidapi::hidraw) | ||
| list(APPEND HIDAPI_HIDTEST_TARGETS hidtest_hidraw) | ||
| endif() | ||
| if(TARGET hidapi::libusb) | ||
| add_executable(hidtest_libusb test.c) | ||
| target_compile_definitions(hidtest_libusb PRIVATE USING_HIDAPI_LIBUSB) | ||
| target_link_libraries(hidtest_libusb hidapi::libusb) | ||
| list(APPEND HIDAPI_HIDTEST_TARGETS hidtest_libusb) | ||
| endif() | ||
| else() | ||
| add_executable(hidtest test.c) | ||
| target_link_libraries(hidtest hidapi::hidapi) | ||
| list(APPEND HIDAPI_HIDTEST_TARGETS hidtest) | ||
| endif() | ||
| install(TARGETS ${HIDAPI_HIDTEST_TARGETS} | ||
| RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}" | ||
| ) |
Sorry, the diff of this file is not supported yet
| /******************************************************* | ||
| HIDAPI - Multi-Platform library for | ||
| communication with HID devices. | ||
| Alan Ott | ||
| Signal 11 Software | ||
| libusb/hidapi Team | ||
| Copyright 2022. | ||
| This contents of this file may be used by anyone | ||
| for any reason without any conditions and may be | ||
| used as a starting point for your own applications | ||
| which use HIDAPI. | ||
| ********************************************************/ | ||
| #include <stdio.h> | ||
| #include <wchar.h> | ||
| #include <string.h> | ||
| #include <stdlib.h> | ||
| #include <hidapi.h> | ||
| // Headers needed for sleeping. | ||
| #ifdef _WIN32 | ||
| #include <windows.h> | ||
| #else | ||
| #include <unistd.h> | ||
| #endif | ||
| // Fallback/example | ||
| #ifndef HID_API_MAKE_VERSION | ||
| #define HID_API_MAKE_VERSION(mj, mn, p) (((mj) << 24) | ((mn) << 8) | (p)) | ||
| #endif | ||
| #ifndef HID_API_VERSION | ||
| #define HID_API_VERSION HID_API_MAKE_VERSION(HID_API_VERSION_MAJOR, HID_API_VERSION_MINOR, HID_API_VERSION_PATCH) | ||
| #endif | ||
| // | ||
| // Sample using platform-specific headers | ||
| #if defined(__APPLE__) && HID_API_VERSION >= HID_API_MAKE_VERSION(0, 12, 0) | ||
| #include <hidapi_darwin.h> | ||
| #endif | ||
| #if defined(_WIN32) && HID_API_VERSION >= HID_API_MAKE_VERSION(0, 12, 0) | ||
| #include <hidapi_winapi.h> | ||
| #endif | ||
| #if defined(USING_HIDAPI_LIBUSB) && HID_API_VERSION >= HID_API_MAKE_VERSION(0, 12, 0) | ||
| #include <hidapi_libusb.h> | ||
| #endif | ||
| // | ||
| const char *hid_bus_name(hid_bus_type bus_type) { | ||
| static const char *const HidBusTypeName[] = { | ||
| "Unknown", | ||
| "USB", | ||
| "Bluetooth", | ||
| "I2C", | ||
| "SPI", | ||
| }; | ||
| if ((int)bus_type < 0) | ||
| bus_type = HID_API_BUS_UNKNOWN; | ||
| if ((int)bus_type >= (int)(sizeof(HidBusTypeName) / sizeof(HidBusTypeName[0]))) | ||
| bus_type = HID_API_BUS_UNKNOWN; | ||
| return HidBusTypeName[bus_type]; | ||
| } | ||
| void print_device(struct hid_device_info *cur_dev) { | ||
| printf("Device Found\n type: %04hx %04hx\n path: %s\n serial_number: %ls", cur_dev->vendor_id, cur_dev->product_id, cur_dev->path, cur_dev->serial_number); | ||
| printf("\n"); | ||
| printf(" Manufacturer: %ls\n", cur_dev->manufacturer_string); | ||
| printf(" Product: %ls\n", cur_dev->product_string); | ||
| printf(" Release: %hx\n", cur_dev->release_number); | ||
| printf(" Interface: %d\n", cur_dev->interface_number); | ||
| printf(" Usage (page): 0x%hx (0x%hx)\n", cur_dev->usage, cur_dev->usage_page); | ||
| printf(" Bus type: %d (%s)\n", cur_dev->bus_type, hid_bus_name(cur_dev->bus_type)); | ||
| printf("\n"); | ||
| } | ||
| void print_hid_report_descriptor_from_device(hid_device *device) { | ||
| unsigned char descriptor[HID_API_MAX_REPORT_DESCRIPTOR_SIZE]; | ||
| int res = 0; | ||
| printf(" Report Descriptor: "); | ||
| res = hid_get_report_descriptor(device, descriptor, sizeof(descriptor)); | ||
| if (res < 0) { | ||
| printf("error getting: %ls", hid_error(device)); | ||
| } | ||
| else { | ||
| printf("(%d bytes)", res); | ||
| } | ||
| for (int i = 0; i < res; i++) { | ||
| if (i % 10 == 0) { | ||
| printf("\n"); | ||
| } | ||
| printf("0x%02x, ", descriptor[i]); | ||
| } | ||
| printf("\n"); | ||
| } | ||
| void print_hid_report_descriptor_from_path(const char *path) { | ||
| hid_device *device = hid_open_path(path); | ||
| if (device) { | ||
| print_hid_report_descriptor_from_device(device); | ||
| hid_close(device); | ||
| } | ||
| else { | ||
| printf(" Report Descriptor: Unable to open device by path\n"); | ||
| } | ||
| } | ||
| void print_devices(struct hid_device_info *cur_dev) { | ||
| for (; cur_dev; cur_dev = cur_dev->next) { | ||
| print_device(cur_dev); | ||
| } | ||
| } | ||
| void print_devices_with_descriptor(struct hid_device_info *cur_dev) { | ||
| for (; cur_dev; cur_dev = cur_dev->next) { | ||
| print_device(cur_dev); | ||
| print_hid_report_descriptor_from_path(cur_dev->path); | ||
| } | ||
| } | ||
| int main(int argc, char* argv[]) | ||
| { | ||
| (void)argc; | ||
| (void)argv; | ||
| int res; | ||
| unsigned char buf[256]; | ||
| #define MAX_STR 255 | ||
| wchar_t wstr[MAX_STR]; | ||
| hid_device *handle; | ||
| int i; | ||
| struct hid_device_info *devs; | ||
| printf("hidapi test/example tool. Compiled with hidapi version %s, runtime version %s.\n", HID_API_VERSION_STR, hid_version_str()); | ||
| if (HID_API_VERSION == HID_API_MAKE_VERSION(hid_version()->major, hid_version()->minor, hid_version()->patch)) { | ||
| printf("Compile-time version matches runtime version of hidapi.\n\n"); | ||
| } | ||
| else { | ||
| printf("Compile-time version is different than runtime version of hidapi.\n]n"); | ||
| } | ||
| if (hid_init()) | ||
| return -1; | ||
| #if defined(__APPLE__) && HID_API_VERSION >= HID_API_MAKE_VERSION(0, 12, 0) | ||
| // To work properly needs to be called before hid_open/hid_open_path after hid_init. | ||
| // Best/recommended option - call it right after hid_init. | ||
| hid_darwin_set_open_exclusive(0); | ||
| #endif | ||
| devs = hid_enumerate(0x0, 0x0); | ||
| print_devices_with_descriptor(devs); | ||
| hid_free_enumeration(devs); | ||
| // Set up the command buffer. | ||
| memset(buf,0x00,sizeof(buf)); | ||
| buf[0] = 0x01; | ||
| buf[1] = 0x81; | ||
| // Open the device using the VID, PID, | ||
| // and optionally the Serial number. | ||
| ////handle = hid_open(0x4d8, 0x3f, L"12345"); | ||
| handle = hid_open(0x4d8, 0x3f, NULL); | ||
| if (!handle) { | ||
| printf("unable to open device\n"); | ||
| hid_exit(); | ||
| return 1; | ||
| } | ||
| // Read the Manufacturer String | ||
| wstr[0] = 0x0000; | ||
| res = hid_get_manufacturer_string(handle, wstr, MAX_STR); | ||
| if (res < 0) | ||
| printf("Unable to read manufacturer string\n"); | ||
| printf("Manufacturer String: %ls\n", wstr); | ||
| // Read the Product String | ||
| wstr[0] = 0x0000; | ||
| res = hid_get_product_string(handle, wstr, MAX_STR); | ||
| if (res < 0) | ||
| printf("Unable to read product string\n"); | ||
| printf("Product String: %ls\n", wstr); | ||
| // Read the Serial Number String | ||
| wstr[0] = 0x0000; | ||
| res = hid_get_serial_number_string(handle, wstr, MAX_STR); | ||
| if (res < 0) | ||
| printf("Unable to read serial number string\n"); | ||
| printf("Serial Number String: (%d) %ls\n", wstr[0], wstr); | ||
| print_hid_report_descriptor_from_device(handle); | ||
| struct hid_device_info* info = hid_get_device_info(handle); | ||
| if (info == NULL) { | ||
| printf("Unable to get device info\n"); | ||
| } else { | ||
| print_devices(info); | ||
| } | ||
| // Read Indexed String 1 | ||
| wstr[0] = 0x0000; | ||
| res = hid_get_indexed_string(handle, 1, wstr, MAX_STR); | ||
| if (res < 0) | ||
| printf("Unable to read indexed string 1\n"); | ||
| printf("Indexed String 1: %ls\n", wstr); | ||
| // Set the hid_read() function to be non-blocking. | ||
| hid_set_nonblocking(handle, 1); | ||
| // Try to read from the device. There should be no | ||
| // data here, but execution should not block. | ||
| res = hid_read(handle, buf, 17); | ||
| // Send a Feature Report to the device | ||
| buf[0] = 0x2; | ||
| buf[1] = 0xa0; | ||
| buf[2] = 0x0a; | ||
| buf[3] = 0x00; | ||
| buf[4] = 0x00; | ||
| res = hid_send_feature_report(handle, buf, 17); | ||
| if (res < 0) { | ||
| printf("Unable to send a feature report.\n"); | ||
| } | ||
| memset(buf,0,sizeof(buf)); | ||
| // Read a Feature Report from the device | ||
| buf[0] = 0x2; | ||
| res = hid_get_feature_report(handle, buf, sizeof(buf)); | ||
| if (res < 0) { | ||
| printf("Unable to get a feature report: %ls\n", hid_error(handle)); | ||
| } | ||
| else { | ||
| // Print out the returned buffer. | ||
| printf("Feature Report\n "); | ||
| for (i = 0; i < res; i++) | ||
| printf("%02x ", (unsigned int) buf[i]); | ||
| printf("\n"); | ||
| } | ||
| memset(buf,0,sizeof(buf)); | ||
| // Toggle LED (cmd 0x80). The first byte is the report number (0x1). | ||
| buf[0] = 0x1; | ||
| buf[1] = 0x80; | ||
| res = hid_write(handle, buf, 17); | ||
| if (res < 0) { | ||
| printf("Unable to write(): %ls\n", hid_error(handle)); | ||
| } | ||
| // Request state (cmd 0x81). The first byte is the report number (0x1). | ||
| buf[0] = 0x1; | ||
| buf[1] = 0x81; | ||
| hid_write(handle, buf, 17); | ||
| if (res < 0) { | ||
| printf("Unable to write()/2: %ls\n", hid_error(handle)); | ||
| } | ||
| // Read requested state. hid_read() has been set to be | ||
| // non-blocking by the call to hid_set_nonblocking() above. | ||
| // This loop demonstrates the non-blocking nature of hid_read(). | ||
| res = 0; | ||
| i = 0; | ||
| while (res == 0) { | ||
| res = hid_read(handle, buf, sizeof(buf)); | ||
| if (res == 0) { | ||
| printf("waiting...\n"); | ||
| } | ||
| if (res < 0) { | ||
| printf("Unable to read(): %ls\n", hid_error(handle)); | ||
| break; | ||
| } | ||
| i++; | ||
| if (i >= 10) { /* 10 tries by 500 ms - 5 seconds of waiting*/ | ||
| printf("read() timeout\n"); | ||
| break; | ||
| } | ||
| #ifdef _WIN32 | ||
| Sleep(500); | ||
| #else | ||
| usleep(500*1000); | ||
| #endif | ||
| } | ||
| if (res > 0) { | ||
| printf("Data read:\n "); | ||
| // Print out the returned buffer. | ||
| for (i = 0; i < res; i++) | ||
| printf("%02x ", (unsigned int) buf[i]); | ||
| printf("\n"); | ||
| } | ||
| hid_close(handle); | ||
| /* Free static HIDAPI objects. */ | ||
| hid_exit(); | ||
| #ifdef _WIN32 | ||
| system("pause"); | ||
| #endif | ||
| return 0; | ||
| } |
| Debug | ||
| Release | ||
| *.exp | ||
| *.ilk | ||
| *.lib | ||
| *.suo | ||
| *.vcproj.* | ||
| *.ncb | ||
| *.suo | ||
| *.dll | ||
| *.pdb | ||
| *.o | ||
| *.so | ||
| hidtest-hidraw | ||
| .deps | ||
| .libs | ||
| *.lo | ||
| *.la |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
| # Ignore All, except pkg.m4, and of course this file. | ||
| * | ||
| !.gitignore | ||
| !pkg.m4 | ||
| !ax_pthread.m4 |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
| Debug | ||
| Release | ||
| *.exp | ||
| *.ilk | ||
| *.lib | ||
| *.suo | ||
| *.vcproj.* | ||
| *.ncb | ||
| *.suo | ||
| *.dll | ||
| *.pdb | ||
| *.o | ||
| hidapi-hidtest | ||
| .deps | ||
| .libs | ||
| *.la | ||
| *.lo |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
| *.pc |
| prefix=@prefix@ | ||
| exec_prefix=@exec_prefix@ | ||
| libdir=@libdir@ | ||
| includedir=@includedir@ | ||
| Name: hidapi-hidraw | ||
| Description: C Library for USB/Bluetooth HID device access from Linux, Mac OS X, FreeBSD, and Windows. This is the hidraw implementation. | ||
| URL: https://github.com/libusb/hidapi | ||
| Version: @VERSION@ | ||
| Libs: -L${libdir} -lhidapi-hidraw | ||
| Cflags: -I${includedir}/hidapi |
| prefix=@prefix@ | ||
| exec_prefix=@exec_prefix@ | ||
| libdir=@libdir@ | ||
| includedir=@includedir@ | ||
| Name: hidapi-libusb | ||
| Description: C Library for USB HID device access from Linux, Mac OS X, FreeBSD, and Windows. This is the libusb implementation. | ||
| URL: https://github.com/libusb/hidapi | ||
| Version: @VERSION@ | ||
| Libs: -L${libdir} -lhidapi-libusb | ||
| Cflags: -I${includedir}/hidapi |
| prefix=@prefix@ | ||
| exec_prefix=@exec_prefix@ | ||
| libdir=@libdir@ | ||
| includedir=@includedir@ | ||
| Name: hidapi | ||
| Description: C Library for USB/Bluetooth HID device access from Linux, Mac OS X, FreeBSD, and Windows. | ||
| URL: https://github.com/libusb/hidapi | ||
| Version: @VERSION@ | ||
| Libs: -L${libdir} -lhidapi | ||
| Cflags: -I${includedir}/hidapi |
| cmake_minimum_required(VERSION 3.1.3 FATAL_ERROR) | ||
| project(hidapi LANGUAGES C) | ||
| file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/root") | ||
| foreach(ROOT_ELEMENT CMakeLists.txt hidapi src windows linux mac libusb pc VERSION) | ||
| file(COPY "${CMAKE_CURRENT_LIST_DIR}/../../${ROOT_ELEMENT}" DESTINATION "${CMAKE_CURRENT_BINARY_DIR}/root/") | ||
| endforeach() | ||
| add_subdirectory("${CMAKE_CURRENT_BINARY_DIR}/root" hidapi_root) |
| Debug | ||
| Release | ||
| *.exp | ||
| *.ilk | ||
| *.lib | ||
| *.suo | ||
| *.vcproj.* | ||
| *.ncb | ||
| *.suo | ||
| *.dll | ||
| *.pdb | ||
| *.o | ||
| hidapi-testgui | ||
| hidapi-hidraw-testgui | ||
| hidapi-libusb-testgui | ||
| .deps | ||
| .libs | ||
| *.la | ||
| *.lo | ||
| TestGUI.app |
| #!/bin/bash | ||
| #### Configuration: | ||
| # The name of the executable. It is assumed | ||
| # that it is in the current working directory. | ||
| EXE_NAME=hidapi-testgui | ||
| # Path to the executable directory inside the bundle. | ||
| # This must be an absolute path, so use $PWD. | ||
| EXEPATH=$PWD/TestGUI.app/Contents/MacOS | ||
| # Libraries to explicitly bundle, even though they | ||
| # may not be in /opt/local. One per line. These | ||
| # are used with grep, so only a portion of the name | ||
| # is required. eg: libFOX, libz, etc. | ||
| LIBS_TO_BUNDLE=libFOX | ||
| function copydeps { | ||
| local file=$1 | ||
| # echo "Copying deps for $file...." | ||
| local BASE_OF_EXE=`basename $file` | ||
| # A will contain the dependencies of this library | ||
| local A=`otool -LX $file |cut -f 1 -d " "` | ||
| local i | ||
| for i in $A; do | ||
| local BASE=`basename $i` | ||
| # See if it's a lib we specifically want to bundle | ||
| local bundle_this_lib=0 | ||
| local j | ||
| for j in $LIBS_TO_BUNDLE; do | ||
| echo $i |grep -q $j | ||
| if [ $? -eq 0 ]; then | ||
| bundle_this_lib=1 | ||
| echo "bundling $i because it's in the list." | ||
| break; | ||
| fi | ||
| done | ||
| # See if it's in /opt/local. Bundle all in /opt/local | ||
| local isOptLocal=0 | ||
| echo $i |grep -q /opt/local | ||
| if [ $? -eq 0 ]; then | ||
| isOptLocal=1 | ||
| echo "bundling $i because it's in /opt/local." | ||
| fi | ||
| # Bundle the library | ||
| if [ $isOptLocal -ne 0 ] || [ $bundle_this_lib -ne 0 ]; then | ||
| # Copy the file into the bundle if it exists. | ||
| if [ -f $EXEPATH/$BASE ]; then | ||
| z=0 | ||
| else | ||
| cp $i $EXEPATH | ||
| chmod 755 $EXEPATH/$BASE | ||
| fi | ||
| # echo "$BASE_OF_EXE depends on $BASE" | ||
| # Fix the paths using install_name_tool and then | ||
| # call this function recursively for each dependency | ||
| # of this library. | ||
| if [ $BASE_OF_EXE != $BASE ]; then | ||
| # Fix the paths | ||
| install_name_tool -id @executable_path/$BASE $EXEPATH/$BASE | ||
| install_name_tool -change $i @executable_path/$BASE $EXEPATH/$BASE_OF_EXE | ||
| # Call this function (recursive) on | ||
| # on each dependency of this library. | ||
| copydeps $EXEPATH/$BASE | ||
| fi | ||
| fi | ||
| done | ||
| } | ||
| rm -f $EXEPATH/* | ||
| mkdir -p $EXEPATH | ||
| # Copy the binary into the bundle. Use ../libtool to do this if it's | ||
| # available because if $EXE_NAME was built with autotools, it will be | ||
| # necessary. If ../libtool not available, just use cp to do the copy, but | ||
| # only if $EXE_NAME is a binary. | ||
| if [ -x ../libtool ]; then | ||
| ../libtool --mode=install cp $EXE_NAME $EXEPATH | ||
| else | ||
| file -bI $EXE_NAME |grep binary | ||
| if [ $? -ne 0 ]; then | ||
| echo "There is no ../libtool and $EXE_NAME is not a binary." | ||
| echo "I'm not sure what to do." | ||
| exit 1 | ||
| else | ||
| cp $EXE_NAME $EXEPATH | ||
| fi | ||
| fi | ||
| copydeps $EXEPATH/$EXE_NAME |
| /******************************* | ||
| Mac support for HID Test GUI | ||
| Alan Ott | ||
| Signal 11 Software | ||
| *******************************/ | ||
| #include <fx.h> | ||
| #import <Cocoa/Cocoa.h> | ||
| #ifndef MAC_OS_X_VERSION_10_12 | ||
| #define MAC_OS_X_VERSION_10_12 101200 | ||
| #endif | ||
| // macOS 10.12 deprecated NSAnyEventMask in favor of NSEventMaskAny | ||
| #if MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_12 | ||
| #define NSEventMaskAny NSAnyEventMask | ||
| #endif | ||
| extern FXMainWindow *g_main_window; | ||
| @interface MyAppDelegate : NSObject<NSApplicationDelegate> | ||
| { | ||
| } | ||
| @end | ||
| @implementation MyAppDelegate | ||
| - (void) applicationWillBecomeActive:(NSNotification*)notif | ||
| { | ||
| printf("WillBecomeActive\n"); | ||
| g_main_window->show(); | ||
| } | ||
| - (void) applicationWillTerminate:(NSNotification*)notif | ||
| { | ||
| /* Doesn't get called. Not sure why */ | ||
| printf("WillTerminate\n"); | ||
| FXApp::instance()->exit(); | ||
| } | ||
| - (NSApplicationTerminateReply) applicationShouldTerminate:(NSApplication*)sender | ||
| { | ||
| /* Doesn't get called. Not sure why */ | ||
| printf("ShouldTerminate\n"); | ||
| return YES; | ||
| } | ||
| - (void) applicationWillHide:(NSNotification*)notif | ||
| { | ||
| printf("WillHide\n"); | ||
| g_main_window->hide(); | ||
| } | ||
| - (void) handleQuitEvent:(NSAppleEventDescriptor*)event withReplyEvent:(NSAppleEventDescriptor*)replyEvent | ||
| { | ||
| printf("QuitEvent\n"); | ||
| FXApp::instance()->exit(); | ||
| } | ||
| @end | ||
| extern "C" { | ||
| void | ||
| init_apple_message_system() | ||
| { | ||
| static MyAppDelegate *d = [MyAppDelegate new]; | ||
| [[NSApplication sharedApplication] setDelegate:d]; | ||
| /* Register for Apple Events. */ | ||
| /* This is from | ||
| http://stackoverflow.com/questions/1768497/application-exit-event */ | ||
| NSAppleEventManager *aem = [NSAppleEventManager sharedAppleEventManager]; | ||
| [aem setEventHandler:d | ||
| andSelector:@selector(handleQuitEvent:withReplyEvent:) | ||
| forEventClass:kCoreEventClass andEventID:kAEQuitApplication]; | ||
| } | ||
| void | ||
| check_apple_events() | ||
| { | ||
| NSApplication *app = [NSApplication sharedApplication]; | ||
| NSAutoreleasePool *pool = [NSAutoreleasePool new]; | ||
| while (1) { | ||
| NSEvent* event = [NSApp nextEventMatchingMask:NSEventMaskAny | ||
| untilDate:nil | ||
| inMode:NSDefaultRunLoopMode | ||
| dequeue:YES]; | ||
| if (event == NULL) | ||
| break; | ||
| else { | ||
| //printf("Event happened: Type: %d\n", event->_type); | ||
| [app sendEvent: event]; | ||
| } | ||
| } | ||
| [pool release]; | ||
| } | ||
| } /* extern "C" */ |
| /******************************* | ||
| Mac support for HID Test GUI | ||
| Alan Ott | ||
| Signal 11 Software | ||
| *******************************/ | ||
| #ifndef MAC_SUPPORT_H__ | ||
| #define MAC_SUPPORT_H__ | ||
| extern "C" { | ||
| void init_apple_message_system(); | ||
| void check_apple_events(); | ||
| } | ||
| #endif |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
| /******************************************************* | ||
| Demo Program for HIDAPI | ||
| Alan Ott | ||
| Signal 11 Software | ||
| 2010-07-20 | ||
| Copyright 2010, All Rights Reserved | ||
| This contents of this file may be used by anyone | ||
| for any reason without any conditions and may be | ||
| used as a starting point for your own applications | ||
| which use HIDAPI. | ||
| ********************************************************/ | ||
| #include <fx.h> | ||
| #include "hidapi.h" | ||
| #include "mac_support.h" | ||
| #include <string.h> | ||
| #include <stdlib.h> | ||
| #include <limits.h> | ||
| #ifdef _WIN32 | ||
| // Thanks Microsoft, but I know how to use strncpy(). | ||
| #pragma warning(disable:4996) | ||
| #endif | ||
| class MainWindow : public FXMainWindow { | ||
| FXDECLARE(MainWindow) | ||
| public: | ||
| enum { | ||
| ID_FIRST = FXMainWindow::ID_LAST, | ||
| ID_CONNECT, | ||
| ID_DISCONNECT, | ||
| ID_RESCAN, | ||
| ID_SEND_OUTPUT_REPORT, | ||
| ID_SEND_FEATURE_REPORT, | ||
| ID_GET_FEATURE_REPORT, | ||
| ID_CLEAR, | ||
| ID_TIMER, | ||
| ID_MAC_TIMER, | ||
| ID_LAST, | ||
| }; | ||
| private: | ||
| FXList *device_list; | ||
| FXButton *connect_button; | ||
| FXButton *disconnect_button; | ||
| FXButton *rescan_button; | ||
| FXButton *output_button; | ||
| FXLabel *connected_label; | ||
| FXTextField *output_text; | ||
| FXTextField *output_len; | ||
| FXButton *feature_button; | ||
| FXButton *get_feature_button; | ||
| FXTextField *feature_text; | ||
| FXTextField *feature_len; | ||
| FXTextField *get_feature_text; | ||
| FXText *input_text; | ||
| FXFont *title_font; | ||
| struct hid_device_info *devices; | ||
| hid_device *connected_device; | ||
| size_t getDataFromTextField(FXTextField *tf, char *buf, size_t len); | ||
| int getLengthFromTextField(FXTextField *tf); | ||
| protected: | ||
| MainWindow() {}; | ||
| public: | ||
| MainWindow(FXApp *a); | ||
| ~MainWindow(); | ||
| virtual void create(); | ||
| long onConnect(FXObject *sender, FXSelector sel, void *ptr); | ||
| long onDisconnect(FXObject *sender, FXSelector sel, void *ptr); | ||
| long onRescan(FXObject *sender, FXSelector sel, void *ptr); | ||
| long onSendOutputReport(FXObject *sender, FXSelector sel, void *ptr); | ||
| long onSendFeatureReport(FXObject *sender, FXSelector sel, void *ptr); | ||
| long onGetFeatureReport(FXObject *sender, FXSelector sel, void *ptr); | ||
| long onClear(FXObject *sender, FXSelector sel, void *ptr); | ||
| long onTimeout(FXObject *sender, FXSelector sel, void *ptr); | ||
| long onMacTimeout(FXObject *sender, FXSelector sel, void *ptr); | ||
| }; | ||
| // FOX 1.7 changes the timeouts to all be nanoseconds. | ||
| // Fox 1.6 had all timeouts as milliseconds. | ||
| #if (FOX_MINOR >= 7) | ||
| const int timeout_scalar = 1000*1000; | ||
| #else | ||
| const int timeout_scalar = 1; | ||
| #endif | ||
| FXMainWindow *g_main_window; | ||
| FXDEFMAP(MainWindow) MainWindowMap [] = { | ||
| FXMAPFUNC(SEL_COMMAND, MainWindow::ID_CONNECT, MainWindow::onConnect ), | ||
| FXMAPFUNC(SEL_COMMAND, MainWindow::ID_DISCONNECT, MainWindow::onDisconnect ), | ||
| FXMAPFUNC(SEL_COMMAND, MainWindow::ID_RESCAN, MainWindow::onRescan ), | ||
| FXMAPFUNC(SEL_COMMAND, MainWindow::ID_SEND_OUTPUT_REPORT, MainWindow::onSendOutputReport ), | ||
| FXMAPFUNC(SEL_COMMAND, MainWindow::ID_SEND_FEATURE_REPORT, MainWindow::onSendFeatureReport ), | ||
| FXMAPFUNC(SEL_COMMAND, MainWindow::ID_GET_FEATURE_REPORT, MainWindow::onGetFeatureReport ), | ||
| FXMAPFUNC(SEL_COMMAND, MainWindow::ID_CLEAR, MainWindow::onClear ), | ||
| FXMAPFUNC(SEL_TIMEOUT, MainWindow::ID_TIMER, MainWindow::onTimeout ), | ||
| FXMAPFUNC(SEL_TIMEOUT, MainWindow::ID_MAC_TIMER, MainWindow::onMacTimeout ), | ||
| }; | ||
| FXIMPLEMENT(MainWindow, FXMainWindow, MainWindowMap, ARRAYNUMBER(MainWindowMap)); | ||
| MainWindow::MainWindow(FXApp *app) | ||
| : FXMainWindow(app, "HIDAPI Test Application", NULL, NULL, DECOR_ALL, 200,100, 425,700) | ||
| { | ||
| devices = NULL; | ||
| connected_device = NULL; | ||
| FXVerticalFrame *vf = new FXVerticalFrame(this, LAYOUT_FILL_Y|LAYOUT_FILL_X); | ||
| FXLabel *label = new FXLabel(vf, "HIDAPI Test Tool"); | ||
| title_font = new FXFont(getApp(), "Arial", 14, FXFont::Bold); | ||
| label->setFont(title_font); | ||
| new FXLabel(vf, | ||
| "Select a device and press Connect.", NULL, JUSTIFY_LEFT); | ||
| new FXLabel(vf, | ||
| "Output data bytes can be entered in the Output section, \n" | ||
| "separated by space, comma or brackets. Data starting with 0x\n" | ||
| "is treated as hex. Data beginning with a 0 is treated as \n" | ||
| "octal. All other data is treated as decimal.", NULL, JUSTIFY_LEFT); | ||
| new FXLabel(vf, | ||
| "Data received from the device appears in the Input section.", | ||
| NULL, JUSTIFY_LEFT); | ||
| new FXLabel(vf, | ||
| "Optionally, a report length may be specified. Extra bytes are\n" | ||
| "padded with zeros. If no length is specified, the length is \n" | ||
| "inferred from the data.", | ||
| NULL, JUSTIFY_LEFT); | ||
| new FXLabel(vf, ""); | ||
| // Device List and Connect/Disconnect buttons | ||
| FXHorizontalFrame *hf = new FXHorizontalFrame(vf, LAYOUT_FILL_X); | ||
| //device_list = new FXList(new FXHorizontalFrame(hf,FRAME_SUNKEN|FRAME_THICK, 0,0,0,0, 0,0,0,0), NULL, 0, LISTBOX_NORMAL|LAYOUT_FILL_X|LAYOUT_FILL_Y|LAYOUT_FIX_WIDTH|LAYOUT_FIX_HEIGHT, 0,0,300,200); | ||
| device_list = new FXList(new FXHorizontalFrame(hf,FRAME_SUNKEN|FRAME_THICK|LAYOUT_FILL_X|LAYOUT_FILL_Y, 0,0,0,0, 0,0,0,0), NULL, 0, LISTBOX_NORMAL|LAYOUT_FILL_X|LAYOUT_FILL_Y, 0,0,300,200); | ||
| FXVerticalFrame *buttonVF = new FXVerticalFrame(hf); | ||
| connect_button = new FXButton(buttonVF, "Connect", NULL, this, ID_CONNECT, BUTTON_NORMAL|LAYOUT_FILL_X); | ||
| disconnect_button = new FXButton(buttonVF, "Disconnect", NULL, this, ID_DISCONNECT, BUTTON_NORMAL|LAYOUT_FILL_X); | ||
| disconnect_button->disable(); | ||
| rescan_button = new FXButton(buttonVF, "Re-Scan devices", NULL, this, ID_RESCAN, BUTTON_NORMAL|LAYOUT_FILL_X); | ||
| new FXHorizontalFrame(buttonVF, 0, 0,0,0,0, 0,0,50,0); | ||
| connected_label = new FXLabel(vf, "Disconnected"); | ||
| new FXHorizontalFrame(vf); | ||
| // Output Group Box | ||
| FXGroupBox *gb = new FXGroupBox(vf, "Output", FRAME_GROOVE|LAYOUT_FILL_X); | ||
| FXMatrix *matrix = new FXMatrix(gb, 3, MATRIX_BY_COLUMNS|LAYOUT_FILL_X); | ||
| new FXLabel(matrix, "Data"); | ||
| new FXLabel(matrix, "Length"); | ||
| new FXLabel(matrix, ""); | ||
| //hf = new FXHorizontalFrame(gb, LAYOUT_FILL_X); | ||
| output_text = new FXTextField(matrix, 30, NULL, 0, TEXTFIELD_NORMAL|LAYOUT_FILL_X|LAYOUT_FILL_COLUMN); | ||
| output_text->setText("1 0x81 0"); | ||
| output_len = new FXTextField(matrix, 5, NULL, 0, TEXTFIELD_NORMAL|LAYOUT_FILL_X|LAYOUT_FILL_COLUMN); | ||
| output_button = new FXButton(matrix, "Send Output Report", NULL, this, ID_SEND_OUTPUT_REPORT, BUTTON_NORMAL|LAYOUT_FILL_X); | ||
| output_button->disable(); | ||
| //new FXHorizontalFrame(matrix, LAYOUT_FILL_X); | ||
| //hf = new FXHorizontalFrame(gb, LAYOUT_FILL_X); | ||
| feature_text = new FXTextField(matrix, 30, NULL, 0, TEXTFIELD_NORMAL|LAYOUT_FILL_X|LAYOUT_FILL_COLUMN); | ||
| feature_len = new FXTextField(matrix, 5, NULL, 0, TEXTFIELD_NORMAL|LAYOUT_FILL_X|LAYOUT_FILL_COLUMN); | ||
| feature_button = new FXButton(matrix, "Send Feature Report", NULL, this, ID_SEND_FEATURE_REPORT, BUTTON_NORMAL|LAYOUT_FILL_X); | ||
| feature_button->disable(); | ||
| get_feature_text = new FXTextField(matrix, 30, NULL, 0, TEXTFIELD_NORMAL|LAYOUT_FILL_X|LAYOUT_FILL_COLUMN); | ||
| new FXWindow(matrix); | ||
| get_feature_button = new FXButton(matrix, "Get Feature Report", NULL, this, ID_GET_FEATURE_REPORT, BUTTON_NORMAL|LAYOUT_FILL_X); | ||
| get_feature_button->disable(); | ||
| // Input Group Box | ||
| gb = new FXGroupBox(vf, "Input", FRAME_GROOVE|LAYOUT_FILL_X|LAYOUT_FILL_Y); | ||
| FXVerticalFrame *innerVF = new FXVerticalFrame(gb, LAYOUT_FILL_X|LAYOUT_FILL_Y); | ||
| input_text = new FXText(new FXHorizontalFrame(innerVF,LAYOUT_FILL_X|LAYOUT_FILL_Y|FRAME_SUNKEN|FRAME_THICK, 0,0,0,0, 0,0,0,0), NULL, 0, LAYOUT_FILL_X|LAYOUT_FILL_Y); | ||
| input_text->setEditable(false); | ||
| new FXButton(innerVF, "Clear", NULL, this, ID_CLEAR, BUTTON_NORMAL|LAYOUT_RIGHT); | ||
| } | ||
| MainWindow::~MainWindow() | ||
| { | ||
| if (connected_device) | ||
| hid_close(connected_device); | ||
| hid_exit(); | ||
| delete title_font; | ||
| } | ||
| void | ||
| MainWindow::create() | ||
| { | ||
| FXMainWindow::create(); | ||
| show(); | ||
| onRescan(NULL, 0, NULL); | ||
| #ifdef __APPLE__ | ||
| init_apple_message_system(); | ||
| #endif | ||
| getApp()->addTimeout(this, ID_MAC_TIMER, | ||
| 50 * timeout_scalar /*50ms*/); | ||
| } | ||
| long | ||
| MainWindow::onConnect(FXObject *sender, FXSelector sel, void *ptr) | ||
| { | ||
| if (connected_device != NULL) | ||
| return 1; | ||
| FXint cur_item = device_list->getCurrentItem(); | ||
| if (cur_item < 0) | ||
| return -1; | ||
| FXListItem *item = device_list->getItem(cur_item); | ||
| if (!item) | ||
| return -1; | ||
| struct hid_device_info *device_info = (struct hid_device_info*) item->getData(); | ||
| if (!device_info) | ||
| return -1; | ||
| connected_device = hid_open_path(device_info->path); | ||
| if (!connected_device) { | ||
| FXMessageBox::error(this, MBOX_OK, "Device Error", "Unable To Connect to Device"); | ||
| return -1; | ||
| } | ||
| hid_set_nonblocking(connected_device, 1); | ||
| getApp()->addTimeout(this, ID_TIMER, | ||
| 5 * timeout_scalar /*5ms*/); | ||
| FXString s; | ||
| s.format("Connected to: %04hx:%04hx -", device_info->vendor_id, device_info->product_id); | ||
| s += FXString(" ") + device_info->manufacturer_string; | ||
| s += FXString(" ") + device_info->product_string; | ||
| connected_label->setText(s); | ||
| output_button->enable(); | ||
| feature_button->enable(); | ||
| get_feature_button->enable(); | ||
| connect_button->disable(); | ||
| disconnect_button->enable(); | ||
| input_text->setText(""); | ||
| return 1; | ||
| } | ||
| long | ||
| MainWindow::onDisconnect(FXObject *sender, FXSelector sel, void *ptr) | ||
| { | ||
| hid_close(connected_device); | ||
| connected_device = NULL; | ||
| connected_label->setText("Disconnected"); | ||
| output_button->disable(); | ||
| feature_button->disable(); | ||
| get_feature_button->disable(); | ||
| connect_button->enable(); | ||
| disconnect_button->disable(); | ||
| getApp()->removeTimeout(this, ID_TIMER); | ||
| return 1; | ||
| } | ||
| long | ||
| MainWindow::onRescan(FXObject *sender, FXSelector sel, void *ptr) | ||
| { | ||
| struct hid_device_info *cur_dev; | ||
| device_list->clearItems(); | ||
| // List the Devices | ||
| hid_free_enumeration(devices); | ||
| devices = hid_enumerate(0x0, 0x0); | ||
| cur_dev = devices; | ||
| while (cur_dev) { | ||
| // Add it to the List Box. | ||
| FXString s; | ||
| FXString usage_str; | ||
| s.format("%04hx:%04hx -", cur_dev->vendor_id, cur_dev->product_id); | ||
| s += FXString(" ") + cur_dev->manufacturer_string; | ||
| s += FXString(" ") + cur_dev->product_string; | ||
| usage_str.format(" (usage: %04hx:%04hx) ", cur_dev->usage_page, cur_dev->usage); | ||
| s += usage_str; | ||
| FXListItem *li = new FXListItem(s, NULL, cur_dev); | ||
| device_list->appendItem(li); | ||
| cur_dev = cur_dev->next; | ||
| } | ||
| if (device_list->getNumItems() == 0) | ||
| device_list->appendItem("*** No Devices Connected ***"); | ||
| else { | ||
| device_list->selectItem(0); | ||
| } | ||
| return 1; | ||
| } | ||
| size_t | ||
| MainWindow::getDataFromTextField(FXTextField *tf, char *buf, size_t len) | ||
| { | ||
| const char *delim = " ,{}\t\r\n"; | ||
| FXString data = tf->getText(); | ||
| const FXchar *d = data.text(); | ||
| size_t i = 0; | ||
| // Copy the string from the GUI. | ||
| size_t sz = strlen(d); | ||
| char *str = (char*) malloc(sz+1); | ||
| strcpy(str, d); | ||
| // For each token in the string, parse and store in buf[]. | ||
| char *token = strtok(str, delim); | ||
| while (token) { | ||
| char *endptr; | ||
| long int val = strtol(token, &endptr, 0); | ||
| buf[i++] = val; | ||
| token = strtok(NULL, delim); | ||
| } | ||
| free(str); | ||
| return i; | ||
| } | ||
| /* getLengthFromTextField() | ||
| Returns length: | ||
| 0: empty text field | ||
| >0: valid length | ||
| -1: invalid length */ | ||
| int | ||
| MainWindow::getLengthFromTextField(FXTextField *tf) | ||
| { | ||
| long int len; | ||
| FXString str = tf->getText(); | ||
| size_t sz = str.length(); | ||
| if (sz > 0) { | ||
| char *endptr; | ||
| len = strtol(str.text(), &endptr, 0); | ||
| if (endptr != str.text() && *endptr == '\0') { | ||
| if (len <= 0) { | ||
| FXMessageBox::error(this, MBOX_OK, "Invalid length", "Enter a length greater than zero."); | ||
| return -1; | ||
| } | ||
| return len; | ||
| } | ||
| else | ||
| return -1; | ||
| } | ||
| return 0; | ||
| } | ||
| long | ||
| MainWindow::onSendOutputReport(FXObject *sender, FXSelector sel, void *ptr) | ||
| { | ||
| char buf[256]; | ||
| size_t data_len, len; | ||
| int textfield_len; | ||
| memset(buf, 0x0, sizeof(buf)); | ||
| textfield_len = getLengthFromTextField(output_len); | ||
| data_len = getDataFromTextField(output_text, buf, sizeof(buf)); | ||
| if (textfield_len < 0) { | ||
| FXMessageBox::error(this, MBOX_OK, "Invalid length", "Length field is invalid. Please enter a number in hex, octal, or decimal."); | ||
| return 1; | ||
| } | ||
| if (textfield_len > sizeof(buf)) { | ||
| FXMessageBox::error(this, MBOX_OK, "Invalid length", "Length field is too long."); | ||
| return 1; | ||
| } | ||
| len = (textfield_len)? textfield_len: data_len; | ||
| int res = hid_write(connected_device, (const unsigned char*)buf, len); | ||
| if (res < 0) { | ||
| FXMessageBox::error(this, MBOX_OK, "Error Writing", "Could not write to device. Error reported was: %ls", hid_error(connected_device)); | ||
| } | ||
| return 1; | ||
| } | ||
| long | ||
| MainWindow::onSendFeatureReport(FXObject *sender, FXSelector sel, void *ptr) | ||
| { | ||
| char buf[256]; | ||
| size_t data_len, len; | ||
| int textfield_len; | ||
| memset(buf, 0x0, sizeof(buf)); | ||
| textfield_len = getLengthFromTextField(feature_len); | ||
| data_len = getDataFromTextField(feature_text, buf, sizeof(buf)); | ||
| if (textfield_len < 0) { | ||
| FXMessageBox::error(this, MBOX_OK, "Invalid length", "Length field is invalid. Please enter a number in hex, octal, or decimal."); | ||
| return 1; | ||
| } | ||
| if (textfield_len > sizeof(buf)) { | ||
| FXMessageBox::error(this, MBOX_OK, "Invalid length", "Length field is too long."); | ||
| return 1; | ||
| } | ||
| len = (textfield_len)? textfield_len: data_len; | ||
| int res = hid_send_feature_report(connected_device, (const unsigned char*)buf, len); | ||
| if (res < 0) { | ||
| FXMessageBox::error(this, MBOX_OK, "Error Writing", "Could not send feature report to device. Error reported was: %ls", hid_error(connected_device)); | ||
| } | ||
| return 1; | ||
| } | ||
| long | ||
| MainWindow::onGetFeatureReport(FXObject *sender, FXSelector sel, void *ptr) | ||
| { | ||
| char buf[256]; | ||
| size_t len; | ||
| memset(buf, 0x0, sizeof(buf)); | ||
| len = getDataFromTextField(get_feature_text, buf, sizeof(buf)); | ||
| if (len != 1) { | ||
| FXMessageBox::error(this, MBOX_OK, "Too many numbers", "Enter only a single report number in the text field"); | ||
| } | ||
| int res = hid_get_feature_report(connected_device, (unsigned char*)buf, sizeof(buf)); | ||
| if (res < 0) { | ||
| FXMessageBox::error(this, MBOX_OK, "Error Getting Report", "Could not get feature report from device. Error reported was: %ls", hid_error(connected_device)); | ||
| } | ||
| if (res > 0) { | ||
| FXString s; | ||
| s.format("Returned Feature Report. %d bytes:\n", res); | ||
| for (int i = 0; i < res; i++) { | ||
| FXString t; | ||
| t.format("%02hhx ", buf[i]); | ||
| s += t; | ||
| if ((i+1) % 4 == 0) | ||
| s += " "; | ||
| if ((i+1) % 16 == 0) | ||
| s += "\n"; | ||
| } | ||
| s += "\n"; | ||
| input_text->appendText(s); | ||
| input_text->setBottomLine(INT_MAX); | ||
| } | ||
| return 1; | ||
| } | ||
| long | ||
| MainWindow::onClear(FXObject *sender, FXSelector sel, void *ptr) | ||
| { | ||
| input_text->setText(""); | ||
| return 1; | ||
| } | ||
| long | ||
| MainWindow::onTimeout(FXObject *sender, FXSelector sel, void *ptr) | ||
| { | ||
| unsigned char buf[256]; | ||
| int res = hid_read(connected_device, buf, sizeof(buf)); | ||
| if (res > 0) { | ||
| FXString s; | ||
| s.format("Received %d bytes:\n", res); | ||
| for (int i = 0; i < res; i++) { | ||
| FXString t; | ||
| t.format("%02hhx ", buf[i]); | ||
| s += t; | ||
| if ((i+1) % 4 == 0) | ||
| s += " "; | ||
| if ((i+1) % 16 == 0) | ||
| s += "\n"; | ||
| } | ||
| s += "\n"; | ||
| input_text->appendText(s); | ||
| input_text->setBottomLine(INT_MAX); | ||
| } | ||
| if (res < 0) { | ||
| input_text->appendText("hid_read() returned error\n"); | ||
| input_text->setBottomLine(INT_MAX); | ||
| } | ||
| getApp()->addTimeout(this, ID_TIMER, | ||
| 5 * timeout_scalar /*5ms*/); | ||
| return 1; | ||
| } | ||
| long | ||
| MainWindow::onMacTimeout(FXObject *sender, FXSelector sel, void *ptr) | ||
| { | ||
| #ifdef __APPLE__ | ||
| check_apple_events(); | ||
| getApp()->addTimeout(this, ID_MAC_TIMER, | ||
| 50 * timeout_scalar /*50ms*/); | ||
| #endif | ||
| return 1; | ||
| } | ||
| int main(int argc, char **argv) | ||
| { | ||
| FXApp app("HIDAPI Test Application", "Signal 11 Software"); | ||
| app.init(argc, argv); | ||
| g_main_window = new MainWindow(&app); | ||
| app.create(); | ||
| app.run(); | ||
| return 0; | ||
| } |
| <?xml version="1.0" encoding="UTF-8"?> | ||
| <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> | ||
| <plist version="1.0"> | ||
| <dict> | ||
| <key>CFBundleDevelopmentRegion</key> | ||
| <string>English</string> | ||
| <key>CFBundleDisplayName</key> | ||
| <string></string> | ||
| <key>CFBundleExecutable</key> | ||
| <string>hidapi-testgui</string> | ||
| <key>CFBundleIconFile</key> | ||
| <string>Signal11.icns</string> | ||
| <key>CFBundleIdentifier</key> | ||
| <string>us.signal11.hidtestgui</string> | ||
| <key>CFBundleInfoDictionaryVersion</key> | ||
| <string>6.0</string> | ||
| <key>CFBundleName</key> | ||
| <string>testgui</string> | ||
| <key>CFBundlePackageType</key> | ||
| <string>APPL</string> | ||
| <key>CFBundleSignature</key> | ||
| <string>????</string> | ||
| <key>CFBundleVersion</key> | ||
| <string>1.0</string> | ||
| <key>CSResourcesFileMapped</key> | ||
| <true/> | ||
| </dict> | ||
| </plist> |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
| | ||
| Microsoft Visual Studio Solution File, Format Version 10.00 | ||
| # Visual C++ Express 2008 | ||
| Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "testgui", "testgui.vcproj", "{08769AC3-785A-4DDC-BFC7-1775414B7AB7}" | ||
| EndProject | ||
| Global | ||
| GlobalSection(SolutionConfigurationPlatforms) = preSolution | ||
| Debug|Win32 = Debug|Win32 | ||
| Release|Win32 = Release|Win32 | ||
| EndGlobalSection | ||
| GlobalSection(ProjectConfigurationPlatforms) = postSolution | ||
| {08769AC3-785A-4DDC-BFC7-1775414B7AB7}.Debug|Win32.ActiveCfg = Debug|Win32 | ||
| {08769AC3-785A-4DDC-BFC7-1775414B7AB7}.Debug|Win32.Build.0 = Debug|Win32 | ||
| {08769AC3-785A-4DDC-BFC7-1775414B7AB7}.Release|Win32.ActiveCfg = Release|Win32 | ||
| {08769AC3-785A-4DDC-BFC7-1775414B7AB7}.Release|Win32.Build.0 = Release|Win32 | ||
| EndGlobalSection | ||
| GlobalSection(SolutionProperties) = preSolution | ||
| HideSolutionNode = FALSE | ||
| EndGlobalSection | ||
| EndGlobal |
Sorry, the diff of this file is not supported yet
| Debug | ||
| Release | ||
| .vs/ | ||
| *.exp | ||
| *.ilk | ||
| *.lib | ||
| *.suo | ||
| *.vcproj.* | ||
| *.vcxproj.* | ||
| *.ncb | ||
| *.suo | ||
| *.dll | ||
| *.pdb | ||
| .deps | ||
| .libs | ||
| *.lo | ||
| *.la |
Sorry, the diff of this file is not supported yet
| | ||
| Microsoft Visual Studio Solution File, Format Version 12.00 | ||
| # Visual Studio 15 | ||
| VisualStudioVersion = 15.0.28307.136 | ||
| MinimumVisualStudioVersion = 10.0.40219.1 | ||
| Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "hidapi", "hidapi.vcxproj", "{A107C21C-418A-4697-BB10-20C3AA60E2E4}" | ||
| EndProject | ||
| Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "hidtest", "hidtest.vcxproj", "{23E9FF6A-49D1-4993-B2B5-BBB992C6C712}" | ||
| EndProject | ||
| Global | ||
| GlobalSection(SolutionConfigurationPlatforms) = preSolution | ||
| Debug|Win32 = Debug|Win32 | ||
| Debug|x64 = Debug|x64 | ||
| Release|Win32 = Release|Win32 | ||
| Release|x64 = Release|x64 | ||
| EndGlobalSection | ||
| GlobalSection(ProjectConfigurationPlatforms) = postSolution | ||
| {A107C21C-418A-4697-BB10-20C3AA60E2E4}.Debug|Win32.ActiveCfg = Debug|Win32 | ||
| {A107C21C-418A-4697-BB10-20C3AA60E2E4}.Debug|Win32.Build.0 = Debug|Win32 | ||
| {A107C21C-418A-4697-BB10-20C3AA60E2E4}.Debug|x64.ActiveCfg = Debug|x64 | ||
| {A107C21C-418A-4697-BB10-20C3AA60E2E4}.Debug|x64.Build.0 = Debug|x64 | ||
| {A107C21C-418A-4697-BB10-20C3AA60E2E4}.Release|Win32.ActiveCfg = Release|Win32 | ||
| {A107C21C-418A-4697-BB10-20C3AA60E2E4}.Release|Win32.Build.0 = Release|Win32 | ||
| {A107C21C-418A-4697-BB10-20C3AA60E2E4}.Release|x64.ActiveCfg = Release|x64 | ||
| {A107C21C-418A-4697-BB10-20C3AA60E2E4}.Release|x64.Build.0 = Release|x64 | ||
| {23E9FF6A-49D1-4993-B2B5-BBB992C6C712}.Debug|Win32.ActiveCfg = Debug|Win32 | ||
| {23E9FF6A-49D1-4993-B2B5-BBB992C6C712}.Debug|Win32.Build.0 = Debug|Win32 | ||
| {23E9FF6A-49D1-4993-B2B5-BBB992C6C712}.Debug|x64.ActiveCfg = Debug|x64 | ||
| {23E9FF6A-49D1-4993-B2B5-BBB992C6C712}.Debug|x64.Build.0 = Debug|x64 | ||
| {23E9FF6A-49D1-4993-B2B5-BBB992C6C712}.Release|Win32.ActiveCfg = Release|Win32 | ||
| {23E9FF6A-49D1-4993-B2B5-BBB992C6C712}.Release|Win32.Build.0 = Release|Win32 | ||
| {23E9FF6A-49D1-4993-B2B5-BBB992C6C712}.Release|x64.ActiveCfg = Release|x64 | ||
| {23E9FF6A-49D1-4993-B2B5-BBB992C6C712}.Release|x64.Build.0 = Release|x64 | ||
| EndGlobalSection | ||
| GlobalSection(SolutionProperties) = preSolution | ||
| HideSolutionNode = FALSE | ||
| EndGlobalSection | ||
| GlobalSection(ExtensibilityGlobals) = postSolution | ||
| SolutionGuid = {8749E535-9C65-4A89-840E-78D7578C7866} | ||
| EndGlobalSection | ||
| EndGlobal |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
| project(pp_data_dump C) | ||
| add_executable(pp_data_dump pp_data_dump.c) | ||
| set_target_properties(pp_data_dump | ||
| PROPERTIES | ||
| C_STANDARD 11 | ||
| C_STANDARD_REQUIRED TRUE | ||
| ) | ||
| target_link_libraries(pp_data_dump | ||
| PRIVATE hidapi_winapi | ||
| ) | ||
| install(TARGETS pp_data_dump | ||
| RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}" | ||
| ) |
| #include <hid.c> | ||
| #include <../windows/hidapi_descriptor_reconstruct.h> | ||
| #include <hidapi.h> | ||
| #if defined(__MINGW32__) | ||
| #pragma GCC diagnostic ignored "-Wformat" | ||
| #pragma GCC diagnostic ignored "-Wformat-extra-args" | ||
| #endif | ||
| void dump_hid_pp_cap(FILE* file, phid_pp_cap pp_cap, unsigned int cap_idx) { | ||
| fprintf(file, "pp_data->cap[%d]->UsagePage = 0x%04hX\n", cap_idx, pp_cap->UsagePage); | ||
| fprintf(file, "pp_data->cap[%d]->ReportID = 0x%02hhX\n", cap_idx, pp_cap->ReportID); | ||
| fprintf(file, "pp_data->cap[%d]->BitPosition = %hhu\n", cap_idx, pp_cap->BitPosition); | ||
| fprintf(file, "pp_data->cap[%d]->BitSize = %hu\n", cap_idx, pp_cap->ReportSize); | ||
| fprintf(file, "pp_data->cap[%d]->ReportCount = %hu\n", cap_idx, pp_cap->ReportCount); | ||
| fprintf(file, "pp_data->cap[%d]->BytePosition = 0x%04hX\n", cap_idx, pp_cap->BytePosition); | ||
| fprintf(file, "pp_data->cap[%d]->BitCount = %hu\n", cap_idx, pp_cap->BitCount); | ||
| fprintf(file, "pp_data->cap[%d]->BitField = 0x%02lX\n", cap_idx, pp_cap->BitField); | ||
| fprintf(file, "pp_data->cap[%d]->NextBytePosition = 0x%04hX\n", cap_idx, pp_cap->NextBytePosition); | ||
| fprintf(file, "pp_data->cap[%d]->LinkCollection = 0x%04hX\n", cap_idx, pp_cap->LinkCollection); | ||
| fprintf(file, "pp_data->cap[%d]->LinkUsagePage = 0x%04hX\n", cap_idx, pp_cap->LinkUsagePage); | ||
| fprintf(file, "pp_data->cap[%d]->LinkUsage = 0x%04hX\n", cap_idx, pp_cap->LinkUsage); | ||
| // 8 Flags in one byte | ||
| fprintf(file, "pp_data->cap[%d]->IsMultipleItemsForArray = %hhu\n", cap_idx, pp_cap->IsMultipleItemsForArray); | ||
| fprintf(file, "pp_data->cap[%d]->IsButtonCap = %hhu\n", cap_idx, pp_cap->IsButtonCap); | ||
| fprintf(file, "pp_data->cap[%d]->IsPadding = %hhu\n", cap_idx, pp_cap->IsPadding); | ||
| fprintf(file, "pp_data->cap[%d]->IsAbsolute = %hhu\n", cap_idx, pp_cap->IsAbsolute); | ||
| fprintf(file, "pp_data->cap[%d]->IsRange = %hhu\n", cap_idx, pp_cap->IsRange); | ||
| fprintf(file, "pp_data->cap[%d]->IsAlias = %hhu\n", cap_idx, pp_cap->IsAlias); | ||
| fprintf(file, "pp_data->cap[%d]->IsStringRange = %hhu\n", cap_idx, pp_cap->IsStringRange); | ||
| fprintf(file, "pp_data->cap[%d]->IsDesignatorRange = %hhu\n", cap_idx, pp_cap->IsDesignatorRange); | ||
| fprintf(file, "pp_data->cap[%d]->Reserved1 = 0x%02hhX%02hhX%02hhX\n", cap_idx, pp_cap->Reserved1[0], pp_cap->Reserved1[1], pp_cap->Reserved1[2]); | ||
| for (int token_idx = 0; token_idx < 4; token_idx++) { | ||
| fprintf(file, "pp_data->cap[%d]->pp_cap->UnknownTokens[%d].Token = 0x%02hhX\n", cap_idx, token_idx, pp_cap->UnknownTokens[token_idx].Token); | ||
| fprintf(file, "pp_data->cap[%d]->pp_cap->UnknownTokens[%d].Reserved = 0x%02hhX%02hhX%02hhX\n", cap_idx, token_idx, pp_cap->UnknownTokens[token_idx].Reserved[0], pp_cap->UnknownTokens[token_idx].Reserved[1], pp_cap->UnknownTokens[token_idx].Reserved[2]); | ||
| fprintf(file, "pp_data->cap[%d]->pp_cap->UnknownTokens[%d].BitField = 0x%08lX\n", cap_idx, token_idx, pp_cap->UnknownTokens[token_idx].BitField); | ||
| } | ||
| if (pp_cap->IsRange) { | ||
| fprintf(file, "pp_data->cap[%d]->Range.UsageMin = 0x%04hX\n", cap_idx, pp_cap->Range.UsageMin); | ||
| fprintf(file, "pp_data->cap[%d]->Range.UsageMax = 0x%04hX\n", cap_idx, pp_cap->Range.UsageMax); | ||
| fprintf(file, "pp_data->cap[%d]->Range.StringMin = %hu\n", cap_idx, pp_cap->Range.StringMin); | ||
| fprintf(file, "pp_data->cap[%d]->Range.StringMax = %hu\n", cap_idx, pp_cap->Range.StringMax); | ||
| fprintf(file, "pp_data->cap[%d]->Range.DesignatorMin = %hu\n", cap_idx, pp_cap->Range.DesignatorMin); | ||
| fprintf(file, "pp_data->cap[%d]->Range.DesignatorMax = %hu\n", cap_idx, pp_cap->Range.DesignatorMax); | ||
| fprintf(file, "pp_data->cap[%d]->Range.DataIndexMin = %hu\n", cap_idx, pp_cap->Range.DataIndexMin); | ||
| fprintf(file, "pp_data->cap[%d]->Range.DataIndexMax = %hu\n", cap_idx, pp_cap->Range.DataIndexMax); | ||
| } | ||
| else { | ||
| fprintf(file, "pp_data->cap[%d]->NotRange.Usage = 0x%04hX\n", cap_idx, pp_cap->NotRange.Usage); | ||
| fprintf(file, "pp_data->cap[%d]->NotRange.Reserved1 = 0x%04hX\n", cap_idx, pp_cap->NotRange.Reserved1); | ||
| fprintf(file, "pp_data->cap[%d]->NotRange.StringIndex = %hu\n", cap_idx, pp_cap->NotRange.StringIndex); | ||
| fprintf(file, "pp_data->cap[%d]->NotRange.Reserved2 = %hu\n", cap_idx, pp_cap->NotRange.Reserved2); | ||
| fprintf(file, "pp_data->cap[%d]->NotRange.DesignatorIndex = %hu\n", cap_idx, pp_cap->NotRange.DesignatorIndex); | ||
| fprintf(file, "pp_data->cap[%d]->NotRange.Reserved3 = %hu\n", cap_idx, pp_cap->NotRange.Reserved3); | ||
| fprintf(file, "pp_data->cap[%d]->NotRange.DataIndex = %hu\n", cap_idx, pp_cap->NotRange.DataIndex); | ||
| fprintf(file, "pp_data->cap[%d]->NotRange.Reserved4 = %hu\n", cap_idx, pp_cap->NotRange.Reserved4); | ||
| } | ||
| if (pp_cap->IsButtonCap) { | ||
| fprintf(file, "pp_data->cap[%d]->Button.LogicalMin = %ld\n", cap_idx, pp_cap->Button.LogicalMin); | ||
| fprintf(file, "pp_data->cap[%d]->Button.LogicalMax = %ld\n", cap_idx, pp_cap->Button.LogicalMax); | ||
| } | ||
| else | ||
| { | ||
| fprintf(file, "pp_data->cap[%d]->NotButton.HasNull = %hhu\n", cap_idx, pp_cap->NotButton.HasNull); | ||
| fprintf(file, "pp_data->cap[%d]->NotButton.Reserved4 = 0x%02hhX%02hhX%02hhX\n", cap_idx, pp_cap->NotButton.Reserved4[0], pp_cap->NotButton.Reserved4[1], pp_cap->NotButton.Reserved4[2]); | ||
| fprintf(file, "pp_data->cap[%d]->NotButton.LogicalMin = %ld\n", cap_idx, pp_cap->NotButton.LogicalMin); | ||
| fprintf(file, "pp_data->cap[%d]->NotButton.LogicalMax = %ld\n", cap_idx, pp_cap->NotButton.LogicalMax); | ||
| fprintf(file, "pp_data->cap[%d]->NotButton.PhysicalMin = %ld\n", cap_idx, pp_cap->NotButton.PhysicalMin); | ||
| fprintf(file, "pp_data->cap[%d]->NotButton.PhysicalMax = %ld\n", cap_idx, pp_cap->NotButton.PhysicalMax); | ||
| }; | ||
| fprintf(file, "pp_data->cap[%d]->Units = %lu\n", cap_idx, pp_cap->Units); | ||
| fprintf(file, "pp_data->cap[%d]->UnitsExp = %lu\n", cap_idx, pp_cap->UnitsExp); | ||
| } | ||
| void dump_hidp_link_collection_node(FILE* file, phid_pp_link_collection_node pcoll, unsigned int coll_idx) { | ||
| fprintf(file, "pp_data->LinkCollectionArray[%d]->LinkUsage = 0x%04hX\n", coll_idx, pcoll->LinkUsage); | ||
| fprintf(file, "pp_data->LinkCollectionArray[%d]->LinkUsagePage = 0x%04hX\n", coll_idx, pcoll->LinkUsagePage); | ||
| fprintf(file, "pp_data->LinkCollectionArray[%d]->Parent = %hu\n", coll_idx, pcoll->Parent); | ||
| fprintf(file, "pp_data->LinkCollectionArray[%d]->NumberOfChildren = %hu\n", coll_idx, pcoll->NumberOfChildren); | ||
| fprintf(file, "pp_data->LinkCollectionArray[%d]->NextSibling = %hu\n", coll_idx, pcoll->NextSibling); | ||
| fprintf(file, "pp_data->LinkCollectionArray[%d]->FirstChild = %hu\n", coll_idx, pcoll->FirstChild); | ||
| fprintf(file, "pp_data->LinkCollectionArray[%d]->CollectionType = %d\n", coll_idx, pcoll->CollectionType); | ||
| fprintf(file, "pp_data->LinkCollectionArray[%d]->IsAlias = %d\n", coll_idx, pcoll->IsAlias); | ||
| fprintf(file, "pp_data->LinkCollectionArray[%d]->Reserved = 0x%08X\n", coll_idx, pcoll->Reserved); | ||
| } | ||
| int dump_pp_data(FILE* file, hid_device* dev) | ||
| { | ||
| BOOL res; | ||
| hidp_preparsed_data* pp_data = NULL; | ||
| res = HidD_GetPreparsedData(dev->device_handle, (PHIDP_PREPARSED_DATA*) &pp_data); | ||
| if (!res) { | ||
| printf("ERROR: HidD_GetPreparsedData failed!"); | ||
| return -1; | ||
| } | ||
| else { | ||
| fprintf(file, "pp_data->MagicKey = 0x%02hhX%02hhX%02hhX%02hhX%02hhX%02hhX%02hhX%02hhX\n", pp_data->MagicKey[0], pp_data->MagicKey[1], pp_data->MagicKey[2], pp_data->MagicKey[3], pp_data->MagicKey[4], pp_data->MagicKey[5], pp_data->MagicKey[6], pp_data->MagicKey[7]); | ||
| fprintf(file, "pp_data->Usage = 0x%04hX\n", pp_data->Usage); | ||
| fprintf(file, "pp_data->UsagePage = 0x%04hX\n", pp_data->UsagePage); | ||
| fprintf(file, "pp_data->Reserved = 0x%04hX%04hX\n", pp_data->Reserved[0], pp_data->Reserved[1]); | ||
| fprintf(file, "# Input caps_info struct:\n"); | ||
| fprintf(file, "pp_data->caps_info[0]->FirstCap = %hu\n", pp_data->caps_info[0].FirstCap); | ||
| fprintf(file, "pp_data->caps_info[0]->LastCap = %hu\n", pp_data->caps_info[0].LastCap); | ||
| fprintf(file, "pp_data->caps_info[0]->NumberOfCaps = %hu\n", pp_data->caps_info[0].NumberOfCaps); | ||
| fprintf(file, "pp_data->caps_info[0]->ReportByteLength = %hu\n", pp_data->caps_info[0].ReportByteLength); | ||
| fprintf(file, "# Output caps_info struct:\n"); | ||
| fprintf(file, "pp_data->caps_info[1]->FirstCap = %hu\n", pp_data->caps_info[1].FirstCap); | ||
| fprintf(file, "pp_data->caps_info[1]->LastCap = %hu\n", pp_data->caps_info[1].LastCap); | ||
| fprintf(file, "pp_data->caps_info[1]->NumberOfCaps = %hu\n", pp_data->caps_info[1].NumberOfCaps); | ||
| fprintf(file, "pp_data->caps_info[1]->ReportByteLength = %hu\n", pp_data->caps_info[1].ReportByteLength); | ||
| fprintf(file, "# Feature caps_info struct:\n"); | ||
| fprintf(file, "pp_data->caps_info[2]->FirstCap = %hu\n", pp_data->caps_info[2].FirstCap); | ||
| fprintf(file, "pp_data->caps_info[2]->LastCap = %hu\n", pp_data->caps_info[2].LastCap); | ||
| fprintf(file, "pp_data->caps_info[2]->NumberOfCaps = %hu\n", pp_data->caps_info[2].NumberOfCaps); | ||
| fprintf(file, "pp_data->caps_info[2]->ReportByteLength = %hu\n", pp_data->caps_info[2].ReportByteLength); | ||
| fprintf(file, "# LinkCollectionArray Offset & Size:\n"); | ||
| fprintf(file, "pp_data->FirstByteOfLinkCollectionArray = 0x%04hX\n", pp_data->FirstByteOfLinkCollectionArray); | ||
| fprintf(file, "pp_data->NumberLinkCollectionNodes = %hu\n", pp_data->NumberLinkCollectionNodes); | ||
| phid_pp_cap pcap = (phid_pp_cap)(((unsigned char*)pp_data) + offsetof(hidp_preparsed_data, caps)); | ||
| fprintf(file, "# Input hid_pp_cap struct:\n"); | ||
| for (int caps_idx = pp_data->caps_info[0].FirstCap; caps_idx < pp_data->caps_info[0].LastCap; caps_idx++) { | ||
| dump_hid_pp_cap(file, pcap + caps_idx, caps_idx); | ||
| fprintf(file, "\n"); | ||
| } | ||
| fprintf(file, "# Output hid_pp_cap struct:\n"); | ||
| for (int caps_idx = pp_data->caps_info[1].FirstCap; caps_idx < pp_data->caps_info[1].LastCap; caps_idx++) { | ||
| dump_hid_pp_cap(file, pcap + caps_idx, caps_idx); | ||
| fprintf(file, "\n"); | ||
| } | ||
| fprintf(file, "# Feature hid_pp_cap struct:\n"); | ||
| for (int caps_idx = pp_data->caps_info[2].FirstCap; caps_idx < pp_data->caps_info[2].LastCap; caps_idx++) { | ||
| dump_hid_pp_cap(file, pcap + caps_idx, caps_idx); | ||
| fprintf(file, "\n"); | ||
| } | ||
| phid_pp_link_collection_node pcoll = (phid_pp_link_collection_node)(((unsigned char*)pcap) + pp_data->FirstByteOfLinkCollectionArray); | ||
| fprintf(file, "# Link Collections:\n"); | ||
| for (int coll_idx = 0; coll_idx < pp_data->NumberLinkCollectionNodes; coll_idx++) { | ||
| dump_hidp_link_collection_node(file, pcoll + coll_idx, coll_idx); | ||
| } | ||
| HidD_FreePreparsedData((PHIDP_PREPARSED_DATA) pp_data); | ||
| return 0; | ||
| } | ||
| } | ||
| int main(int argc, char* argv[]) | ||
| { | ||
| (void)argc; | ||
| (void)argv; | ||
| #define MAX_STR 255 | ||
| struct hid_device_info *devs, *cur_dev; | ||
| printf("pp_data_dump tool. Compiled with hidapi version %s, runtime version %s.\n", HID_API_VERSION_STR, hid_version_str()); | ||
| if (hid_version()->major == HID_API_VERSION_MAJOR && hid_version()->minor == HID_API_VERSION_MINOR && hid_version()->patch == HID_API_VERSION_PATCH) { | ||
| printf("Compile-time version matches runtime version of hidapi.\n\n"); | ||
| } | ||
| else { | ||
| printf("Compile-time version is different than runtime version of hidapi.\n]n"); | ||
| } | ||
| if (hid_init()) | ||
| return -1; | ||
| devs = hid_enumerate(0x0, 0x0); | ||
| cur_dev = devs; | ||
| while (cur_dev) { | ||
| printf("Device Found\n type: %04hx %04hx\n path: %s\n serial_number: %ls", cur_dev->vendor_id, cur_dev->product_id, cur_dev->path, cur_dev->serial_number); | ||
| printf("\n"); | ||
| printf(" Manufacturer: %ls\n", cur_dev->manufacturer_string); | ||
| printf(" Product: %ls\n", cur_dev->product_string); | ||
| printf(" Release: %hX\n", cur_dev->release_number); | ||
| printf(" Interface: %d\n", cur_dev->interface_number); | ||
| printf(" Usage (page): %02X (%02X)\n", cur_dev->usage, cur_dev->usage_page); | ||
| hid_device *device = hid_open_path(cur_dev->path); | ||
| if (device) { | ||
| char filename[MAX_STR]; | ||
| FILE* file; | ||
| sprintf_s(filename, MAX_STR, "%04X_%04X_%04X_%04X.pp_data", cur_dev->vendor_id, cur_dev->product_id, cur_dev->usage, cur_dev->usage_page); | ||
| errno_t err = fopen_s(&file, filename, "w"); | ||
| if (err == 0) { | ||
| fprintf(file, "# HIDAPI device info struct:\n"); | ||
| fprintf(file, "dev->vendor_id = 0x%04hX\n", cur_dev->vendor_id); | ||
| fprintf(file, "dev->product_id = 0x%04hX\n", cur_dev->product_id); | ||
| fprintf(file, "dev->manufacturer_string = \"%ls\"\n", cur_dev->manufacturer_string); | ||
| fprintf(file, "dev->product_string = \"%ls\"\n", cur_dev->product_string); | ||
| fprintf(file, "dev->release_number = 0x%04hX\n", cur_dev->release_number); | ||
| fprintf(file, "dev->interface_number = %d\n", cur_dev->interface_number); | ||
| fprintf(file, "dev->usage = 0x%04hX\n", cur_dev->usage); | ||
| fprintf(file, "dev->usage_page = 0x%04hX\n", cur_dev->usage_page); | ||
| fprintf(file, "dev->path = \"%s\"\n", cur_dev->path); | ||
| fprintf(file, "\n# Preparsed Data struct:\n"); | ||
| int res = dump_pp_data(file, device); | ||
| if (res == 0) { | ||
| printf("Dumped Preparsed Data to %s\n", filename); | ||
| } | ||
| else { | ||
| printf("ERROR: Dump Preparsed Data to %s failed!\n", filename); | ||
| } | ||
| fclose(file); | ||
| } | ||
| hid_close(device); | ||
| } | ||
| else { | ||
| printf(" Device: not available.\n"); | ||
| } | ||
| printf("\n"); | ||
| cur_dev = cur_dev->next; | ||
| } | ||
| hid_free_enumeration(devs); | ||
| /* Free static HIDAPI objects. */ | ||
| hid_exit(); | ||
| //system("pause"); | ||
| return 0; | ||
| } |
| add_executable(hid_report_reconstructor_test hid_report_reconstructor_test.c) | ||
| set_target_properties(hid_report_reconstructor_test | ||
| PROPERTIES | ||
| C_STANDARD 11 | ||
| C_STANDARD_REQUIRED TRUE | ||
| ) | ||
| target_link_libraries(hid_report_reconstructor_test | ||
| PRIVATE hidapi_include hidapi_winapi | ||
| ) | ||
| # Each test case requires 2 files: | ||
| # <name>.pp_data - textual representation of HIDP_PREPARSED_DATA; | ||
| # <name>_expected.rpt_desc - reconstructed HID Report Descriptor out of .pp_data file; | ||
| # | ||
| # (Non-required by test): | ||
| # <name>_real.dpt_desc - the original report rescriptor used to create a test case; | ||
| set(HID_DESCRIPTOR_RECONSTRUCT_TEST_CASES | ||
| 046D_C52F_0001_000C | ||
| 046D_C52F_0001_FF00 | ||
| 046D_C52F_0002_0001 | ||
| 046D_C52F_0002_FF00 | ||
| 17CC_1130_0000_FF01 | ||
| 046D_0A37_0001_000C | ||
| 046A_0011_0006_0001 | ||
| 046D_C077_0002_0001 | ||
| 046D_C283_0004_0001 | ||
| 046D_B010_0006_0001 | ||
| 046D_B010_0002_FF00 | ||
| 046D_B010_0002_0001 | ||
| 046D_B010_0001_FF00 | ||
| 046D_B010_0001_000C | ||
| 046D_C534_0001_000C | ||
| 046D_C534_0001_FF00 | ||
| 046D_C534_0002_0001 | ||
| 046D_C534_0002_FF00 | ||
| 046D_C534_0006_0001 | ||
| 046D_C534_0080_0001 | ||
| 047F_C056_0001_000C | ||
| 047F_C056_0003_FFA0 | ||
| 047F_C056_0005_000B | ||
| 045E_02FF_0005_0001 | ||
| ) | ||
| set(CMAKE_VERSION_SUPPORTS_ENVIRONMENT_MODIFICATION "3.22") | ||
| if(HIDAPI_ENABLE_ASAN AND MSVC) | ||
| if(CMAKE_VERSION VERSION_LESS CMAKE_VERSION_SUPPORTS_ENVIRONMENT_MODIFICATION) | ||
| message("CTest/ASAN: Make sure to run ctest from MSVC Command Prompt") | ||
| endif() | ||
| endif() | ||
| foreach(TEST_CASE ${HID_DESCRIPTOR_RECONSTRUCT_TEST_CASES}) | ||
| set(TEST_PP_DATA "${CMAKE_CURRENT_LIST_DIR}/data/${TEST_CASE}.pp_data") | ||
| if(NOT EXISTS "${TEST_PP_DATA}") | ||
| message(FATAL_ERROR "Missing '${TEST_PP_DATA}' file for '${TEST_CASE}' test case") | ||
| endif() | ||
| set(TEST_EXPECTED_DESCRIPTOR "${CMAKE_CURRENT_LIST_DIR}/data/${TEST_CASE}_expected.rpt_desc") | ||
| if(NOT EXISTS "${TEST_EXPECTED_DESCRIPTOR}") | ||
| message(FATAL_ERROR "Missing '${TEST_EXPECTED_DESCRIPTOR}' file for '${TEST_CASE}' test case") | ||
| endif() | ||
| add_test(NAME "WinHidReportReconstructTest_${TEST_CASE}" | ||
| COMMAND hid_report_reconstructor_test "${TEST_PP_DATA}" "${TEST_EXPECTED_DESCRIPTOR}" | ||
| WORKING_DIRECTORY "$<TARGET_FILE_DIR:hidapi_winapi>" | ||
| ) | ||
| if(HIDAPI_ENABLE_ASAN) | ||
| if(MSVC) | ||
| if(NOT CMAKE_VERSION VERSION_LESS CMAKE_VERSION_SUPPORTS_ENVIRONMENT_MODIFICATION) | ||
| get_filename_component(MSVC_BUILD_TOOLS_DIR "${CMAKE_LINKER}" DIRECTORY) | ||
| set_property(TEST "WinHidReportReconstructTest_${TEST_CASE}" PROPERTY ENVIRONMENT_MODIFICATION "PATH=path_list_append:${MSVC_BUILD_TOOLS_DIR}") | ||
| endif() | ||
| endif() | ||
| set_property(TEST "WinHidReportReconstructTest_${TEST_CASE}" PROPERTY ENVIRONMENT "ASAN_SAVE_DUMPS=AsanDump_${TEST_CASE}.dmp") | ||
| endif() | ||
| endforeach() |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
| #include "../hidapi_descriptor_reconstruct.h" | ||
| #include <stddef.h> | ||
| #include <stdio.h> | ||
| #include <string.h> | ||
| #if defined(__MINGW32__) | ||
| #pragma GCC diagnostic ignored "-Wformat" | ||
| #endif | ||
| static hidp_preparsed_data * alloc_preparsed_data_from_file(char* filename) | ||
| { | ||
| FILE* file; | ||
| errno_t err = fopen_s(&file, filename, "r"); | ||
| if (err != 0) { | ||
| fprintf(stderr, "ERROR: Couldn't open file '%s' for reading: %s\n", filename, strerror(err)); | ||
| return NULL; | ||
| } | ||
| char line[256]; | ||
| { | ||
| unsigned short vendor_id = 0; | ||
| unsigned short product_id = 0; | ||
| unsigned short usage = 0; | ||
| unsigned short usage_page = 0; | ||
| unsigned short release_number = 0; | ||
| int interface_number = -1; | ||
| BOOLEAN header_read_success = FALSE; | ||
| char manufacturer_string[128]; | ||
| manufacturer_string[0] = '\0'; | ||
| char product_string[128]; | ||
| product_string[0] = '\0'; | ||
| // char path[128]; | ||
| // path[0] = '\0'; | ||
| while (fgets(line, sizeof(line), file) != NULL) { | ||
| if (line[0] == '\r' || line[0] == '\n') { | ||
| line[0] = '\0'; | ||
| } | ||
| if (line[0] == '\0') { | ||
| // read the 'metadata' only until the first empty line | ||
| header_read_success = TRUE; | ||
| break; | ||
| } | ||
| if (sscanf(line, "dev->vendor_id = 0x%04hX\n", &vendor_id)) continue; | ||
| if (sscanf(line, "dev->product_id = 0x%04hX\n", &product_id)) continue; | ||
| if (sscanf(line, "dev->usage_page = 0x%04hX\n", &usage_page)) continue; | ||
| if (sscanf(line, "dev->usage = 0x%04hX\n", &usage)) continue; | ||
| if (sscanf(line, "dev->manufacturer_string = \"%127[^\"\n]", manufacturer_string)) continue; | ||
| if (sscanf(line, "dev->product_string = \"%127[^\"\n]", product_string)) continue; | ||
| if (sscanf(line, "dev->release_number = 0x%04hX\n", &release_number)) continue; | ||
| if (sscanf(line, "dev->interface_number = %d\n", &interface_number)) continue; | ||
| // if (sscanf(line, "dev->path = \"%127[^\"]\n", path)) continue; | ||
| } | ||
| if (!header_read_success) { | ||
| fprintf(stderr, "ERROR: Couldn't read PP Data header (missing newline)\n"); | ||
| fclose(file); | ||
| return NULL; | ||
| } | ||
| printf("'Virtual' Device Read: %04hx %04hx\n", vendor_id, product_id); | ||
| if (manufacturer_string[0] != '\0') { | ||
| printf(" Manufacturer: %s\n", manufacturer_string); | ||
| } | ||
| if (product_string[0] != '\0') { | ||
| printf(" Product: %s\n", product_string); | ||
| } | ||
| printf(" Release: %hx\n", release_number); | ||
| printf(" Interface: %d\n", interface_number); | ||
| printf(" Usage (page): 0x%hx (0x%hx)\n", usage, usage_page); | ||
| } | ||
| hidp_preparsed_data static_pp_data; | ||
| memset(&static_pp_data, 0, sizeof(static_pp_data)); | ||
| hidp_preparsed_data *pp_data = &static_pp_data; | ||
| unsigned int rt_idx; | ||
| unsigned int caps_idx; | ||
| unsigned int token_idx; | ||
| unsigned int coll_idx; | ||
| USAGE temp_usage; | ||
| BOOLEAN temp_boolean[3]; | ||
| UCHAR temp_uchar[3]; | ||
| USHORT temp_ushort; | ||
| ULONG temp_ulong; | ||
| LONG temp_long; | ||
| USHORT FirstByteOfLinkCollectionArray = 0; | ||
| USHORT NumberLinkCollectionNodes = 0; | ||
| while (fgets(line, sizeof(line), file) != NULL) { | ||
| if (line[0] == '#') | ||
| continue; | ||
| if (FirstByteOfLinkCollectionArray != 0 && NumberLinkCollectionNodes != 0) { | ||
| size_t size_of_preparsed_data = offsetof(hidp_preparsed_data, caps) + FirstByteOfLinkCollectionArray + (NumberLinkCollectionNodes * sizeof(hid_pp_link_collection_node)); | ||
| pp_data->FirstByteOfLinkCollectionArray = FirstByteOfLinkCollectionArray; | ||
| pp_data->NumberLinkCollectionNodes = NumberLinkCollectionNodes; | ||
| FirstByteOfLinkCollectionArray = 0; | ||
| NumberLinkCollectionNodes = 0; | ||
| pp_data = malloc(size_of_preparsed_data); | ||
| memcpy(pp_data, &static_pp_data, sizeof(static_pp_data)); | ||
| } | ||
| if (sscanf(line, "pp_data->MagicKey = 0x%02hhX%02hhX%02hhX%02hhX%02hhX%02hhX%02hhX%02hhX\n", &pp_data->MagicKey[0], &pp_data->MagicKey[1], &pp_data->MagicKey[2], &pp_data->MagicKey[3], &pp_data->MagicKey[4], &pp_data->MagicKey[5], &pp_data->MagicKey[6], &pp_data->MagicKey[7])) continue; | ||
| if (sscanf(line, "pp_data->Usage = 0x%04hX\n", &pp_data->Usage)) continue; | ||
| if (sscanf(line, "pp_data->UsagePage = 0x%04hX\n", &pp_data->UsagePage)) continue; | ||
| if (sscanf(line, "pp_data->Reserved = 0x%04hX%04hX\n", &pp_data->Reserved[0], &pp_data->Reserved[1])) continue; | ||
| if (sscanf(line, "pp_data->caps_info[%d]", &rt_idx) == 1) { | ||
| const size_t caps_info_count = sizeof(pp_data->caps_info) / sizeof(pp_data->caps_info[0]); | ||
| if (rt_idx >= caps_info_count) { | ||
| fprintf(stderr, "Broken pp_data file, pp_data->caps_info[<idx>] can have at most %zu elements, accessing %ud, (%s)", caps_info_count, rt_idx, line); | ||
| continue; | ||
| } | ||
| if (sscanf(line, "pp_data->caps_info[%d]->FirstCap = %hu\n", &rt_idx, &temp_ushort) == 2) { | ||
| pp_data->caps_info[rt_idx].FirstCap = temp_ushort; | ||
| continue; | ||
| } | ||
| if (sscanf(line, "pp_data->caps_info[%d]->LastCap = %hu\n", &rt_idx, &temp_ushort) == 2) { | ||
| pp_data->caps_info[rt_idx].LastCap = temp_ushort; | ||
| continue; | ||
| } | ||
| if (sscanf(line, "pp_data->caps_info[%d]->NumberOfCaps = %hu\n", &rt_idx, &temp_ushort) == 2) { | ||
| pp_data->caps_info[rt_idx].NumberOfCaps = temp_ushort; | ||
| continue; | ||
| } | ||
| if (sscanf(line, "pp_data->caps_info[%d]->ReportByteLength = %hu\n", &rt_idx, &temp_ushort) == 2) { | ||
| pp_data->caps_info[rt_idx].ReportByteLength = temp_ushort; | ||
| continue; | ||
| } | ||
| fprintf(stderr, "Ignoring unimplemented caps_info field: %s", line); | ||
| continue; | ||
| } | ||
| if (sscanf(line, "pp_data->FirstByteOfLinkCollectionArray = 0x%04hX\n", &FirstByteOfLinkCollectionArray)) { | ||
| continue; | ||
| } | ||
| if (sscanf(line, "pp_data->NumberLinkCollectionNodes = %hu\n", &NumberLinkCollectionNodes)) { | ||
| continue; | ||
| } | ||
| if (sscanf(line, "pp_data->cap[%d]", &caps_idx) == 1) { | ||
| if (pp_data->FirstByteOfLinkCollectionArray == 0) { | ||
| fprintf(stderr, "Error reading pp_data file (%s): FirstByteOfLinkCollectionArray is 0 or not reported yet\n", line); | ||
| continue; | ||
| } | ||
| if ((caps_idx + 1) * sizeof(hid_pp_cap) > pp_data->FirstByteOfLinkCollectionArray) { | ||
| fprintf(stderr, "Error reading pp_data file (%s): the caps index (%u) is out of pp_data bytes boundary (%hu vs %hu)\n", line, caps_idx, (unsigned short) ((caps_idx + 1) * sizeof(hid_pp_cap)), pp_data->FirstByteOfLinkCollectionArray); | ||
| continue; | ||
| } | ||
| if (sscanf(line, "pp_data->cap[%d]->UsagePage = 0x%04hX\n", &caps_idx, &temp_usage) == 2) { | ||
| pp_data->caps[caps_idx].UsagePage = temp_usage; | ||
| continue; | ||
| } | ||
| if (sscanf(line, "pp_data->cap[%d]->ReportID = 0x%02hhX\n", &caps_idx, &temp_uchar[0]) == 2) { | ||
| pp_data->caps[caps_idx].ReportID = temp_uchar[0]; | ||
| continue; | ||
| } | ||
| if (sscanf(line, "pp_data->cap[%d]->BitPosition = %hhu\n", &caps_idx, &temp_uchar[0]) == 2) { | ||
| pp_data->caps[caps_idx].BitPosition = temp_uchar[0]; | ||
| continue; | ||
| } | ||
| if (sscanf(line, "pp_data->cap[%d]->BitSize = %hu\n", &caps_idx, &temp_ushort) == 2) { | ||
| pp_data->caps[caps_idx].ReportSize = temp_ushort; | ||
| continue; | ||
| } | ||
| if (sscanf(line, "pp_data->cap[%d]->ReportCount = %hu\n", &caps_idx, &temp_ushort) == 2) { | ||
| pp_data->caps[caps_idx].ReportCount = temp_ushort; | ||
| continue; | ||
| } | ||
| if (sscanf(line, "pp_data->cap[%d]->BytePosition = 0x%04hX\n", &caps_idx, &temp_ushort) == 2) { | ||
| pp_data->caps[caps_idx].BytePosition = temp_ushort; | ||
| continue; | ||
| } | ||
| if (sscanf(line, "pp_data->cap[%d]->BitCount = %hu\n", &caps_idx, &temp_ushort) == 2) { | ||
| pp_data->caps[caps_idx].BitCount = temp_ushort; | ||
| continue; | ||
| } | ||
| if (sscanf(line, "pp_data->cap[%d]->BitField = 0x%02lX\n", &caps_idx, &temp_ulong) == 2) { | ||
| pp_data->caps[caps_idx].BitField = temp_ulong; | ||
| continue; | ||
| } | ||
| if (sscanf(line, "pp_data->cap[%d]->NextBytePosition = 0x%04hX\n", &caps_idx, &temp_ushort) == 2) { | ||
| pp_data->caps[caps_idx].NextBytePosition = temp_ushort; | ||
| continue; | ||
| } | ||
| if (sscanf(line, "pp_data->cap[%d]->LinkCollection = 0x%04hX\n", &caps_idx, &temp_ushort) == 2) { | ||
| pp_data->caps[caps_idx].LinkCollection = temp_ushort; | ||
| continue; | ||
| } | ||
| if (sscanf(line, "pp_data->cap[%d]->LinkUsagePage = 0x%04hX\n", &caps_idx, &temp_usage) == 2) { | ||
| pp_data->caps[caps_idx].LinkUsagePage = temp_usage; | ||
| continue; | ||
| } | ||
| if (sscanf(line, "pp_data->cap[%d]->LinkUsage = 0x%04hX\n", &caps_idx, &temp_usage) == 2) { | ||
| pp_data->caps[caps_idx].LinkUsage = temp_usage; | ||
| continue; | ||
| } | ||
| // 8 Flags in one byte | ||
| if (sscanf(line, "pp_data->cap[%d]->IsMultipleItemsForArray = %hhu\n", &caps_idx, &temp_boolean[0]) == 2) { | ||
| pp_data->caps[caps_idx].IsMultipleItemsForArray = temp_boolean[0]; | ||
| continue; | ||
| } | ||
| if (sscanf(line, "pp_data->cap[%d]->IsButtonCap = %hhu\n", &caps_idx, &temp_boolean[0]) == 2) { | ||
| pp_data->caps[caps_idx].IsButtonCap = temp_boolean[0]; | ||
| continue; | ||
| } | ||
| if (sscanf(line, "pp_data->cap[%d]->IsPadding = %hhu\n", &caps_idx, &temp_boolean[0]) == 2) { | ||
| pp_data->caps[caps_idx].IsPadding = temp_boolean[0]; | ||
| continue; | ||
| } | ||
| if (sscanf(line, "pp_data->cap[%d]->IsAbsolute = %hhu\n", &caps_idx, &temp_boolean[0]) == 2) { | ||
| pp_data->caps[caps_idx].IsAbsolute = temp_boolean[0]; | ||
| continue; | ||
| } | ||
| if (sscanf(line, "pp_data->cap[%d]->IsRange = %hhu\n", &caps_idx, &temp_boolean[0]) == 2) { | ||
| pp_data->caps[caps_idx].IsRange = temp_boolean[0]; | ||
| continue; | ||
| } | ||
| if (sscanf(line, "pp_data->cap[%d]->IsAlias = %hhu\n", &caps_idx, &temp_boolean[0]) == 2) { | ||
| pp_data->caps[caps_idx].IsAlias = temp_boolean[0]; | ||
| continue; | ||
| } | ||
| if (sscanf(line, "pp_data->cap[%d]->IsStringRange = %hhu\n", &caps_idx, &temp_boolean[0]) == 2) { | ||
| pp_data->caps[caps_idx].IsStringRange = temp_boolean[0]; | ||
| continue; | ||
| } | ||
| if (sscanf(line, "pp_data->cap[%d]->IsDesignatorRange = %hhu\n", &caps_idx, &temp_boolean[0]) == 2) { | ||
| pp_data->caps[caps_idx].IsDesignatorRange = temp_boolean[0]; | ||
| continue; | ||
| } | ||
| if (sscanf(line, "pp_data->cap[%d]->Reserved1 = 0x%hhu%hhu%hhu\n", &caps_idx, &temp_uchar[0], &temp_uchar[1], &temp_uchar[2]) == 4) { | ||
| pp_data->caps[caps_idx].Reserved1[0] = temp_uchar[0]; | ||
| pp_data->caps[caps_idx].Reserved1[1] = temp_uchar[1]; | ||
| pp_data->caps[caps_idx].Reserved1[2] = temp_uchar[2]; | ||
| continue; | ||
| } | ||
| if (sscanf(line, "pp_data->cap[%d]->pp_cap->UnknownTokens[%d]", &caps_idx, &token_idx) == 2) { | ||
| const size_t unknown_tokens_count = sizeof(pp_data->caps[0].UnknownTokens) / sizeof(pp_data->caps[0].UnknownTokens[0]); | ||
| if (token_idx >= unknown_tokens_count) { | ||
| fprintf(stderr, "Broken pp_data file, pp_data->caps[<idx>].UnknownTokens[<idx>] can have at most %zu elements, accessing %ud, (%s)", unknown_tokens_count, token_idx, line); | ||
| continue; | ||
| } | ||
| if (sscanf(line, "pp_data->cap[%d]->pp_cap->UnknownTokens[%d].Token = 0x%02hhX\n", &caps_idx, &token_idx, &temp_uchar[0]) == 3) { | ||
| pp_data->caps[caps_idx].UnknownTokens[token_idx].Token = temp_uchar[0]; | ||
| continue; | ||
| } | ||
| if (sscanf(line, "pp_data->cap[%d]->pp_cap->UnknownTokens[%d].Reserved = 0x%02hhX%02hhX%02hhX\n", &caps_idx, &token_idx, &temp_uchar[0], &temp_uchar[1], &temp_uchar[2]) == 5) { | ||
| pp_data->caps[caps_idx].UnknownTokens[token_idx].Reserved[0] = temp_uchar[0]; | ||
| pp_data->caps[caps_idx].UnknownTokens[token_idx].Reserved[1] = temp_uchar[1]; | ||
| pp_data->caps[caps_idx].UnknownTokens[token_idx].Reserved[2] = temp_uchar[2]; | ||
| continue; | ||
| } | ||
| if (sscanf(line, "pp_data->cap[%d]->pp_cap->UnknownTokens[%d].BitField = 0x%08lX\n", &caps_idx, &token_idx, &temp_ulong) == 3) { | ||
| pp_data->caps[caps_idx].UnknownTokens[token_idx].BitField = temp_ulong; | ||
| continue; | ||
| } | ||
| fprintf(stderr, "Ignoring unimplemented pp_data->cap[]->pp_cap->UnknownTokens field: %s", line); | ||
| continue; | ||
| } | ||
| // Range | ||
| if (sscanf(line, "pp_data->cap[%d]->Range.UsageMin = 0x%04hX\n", &caps_idx, &temp_usage) == 2) { | ||
| pp_data->caps[caps_idx].Range.UsageMin = temp_usage; | ||
| continue; | ||
| } | ||
| if (sscanf(line, "pp_data->cap[%d]->Range.UsageMax = 0x%04hX\n", &caps_idx, &temp_usage) == 2) { | ||
| pp_data->caps[caps_idx].Range.UsageMax = temp_usage; | ||
| continue; | ||
| } | ||
| if (sscanf(line, "pp_data->cap[%d]->Range.StringMin = %hu\n", &caps_idx, &temp_ushort) == 2) { | ||
| pp_data->caps[caps_idx].Range.StringMin = temp_ushort; | ||
| continue; | ||
| } | ||
| if (sscanf(line, "pp_data->cap[%d]->Range.StringMax = %hu\n", &caps_idx, &temp_ushort) == 2) { | ||
| pp_data->caps[caps_idx].Range.StringMax = temp_ushort; | ||
| continue; | ||
| } | ||
| if (sscanf(line, "pp_data->cap[%d]->Range.DesignatorMin = %hu\n", &caps_idx, &temp_ushort) == 2) { | ||
| pp_data->caps[caps_idx].Range.DesignatorMin = temp_ushort; | ||
| continue; | ||
| } | ||
| if (sscanf(line, "pp_data->cap[%d]->Range.DesignatorMax = %hu\n", &caps_idx, &temp_ushort) == 2) { | ||
| pp_data->caps[caps_idx].Range.DesignatorMax = temp_ushort; | ||
| continue; | ||
| } | ||
| if (sscanf(line, "pp_data->cap[%d]->Range.DataIndexMin = %hu\n", &caps_idx, &temp_ushort) == 2) { | ||
| pp_data->caps[caps_idx].Range.DataIndexMin = temp_ushort; | ||
| continue; | ||
| } | ||
| if (sscanf(line, "pp_data->cap[%d]->Range.DataIndexMax = %hu\n", &caps_idx, &temp_ushort) == 2) { | ||
| pp_data->caps[caps_idx].Range.DataIndexMax = temp_ushort; | ||
| continue; | ||
| } | ||
| // NotRange | ||
| if (sscanf(line, "pp_data->cap[%d]->NotRange.Usage = 0x%04hX\n", &caps_idx, &temp_usage) == 2) { | ||
| pp_data->caps[caps_idx].NotRange.Usage = temp_usage; | ||
| continue; | ||
| } | ||
| if (sscanf(line, "pp_data->cap[%d]->NotRange.Reserved1 = 0x%04hX\n", &caps_idx, &temp_usage) == 2) { | ||
| pp_data->caps[caps_idx].NotRange.Reserved1 = temp_usage; | ||
| continue; | ||
| } | ||
| if (sscanf(line, "pp_data->cap[%d]->NotRange.StringIndex = %hu\n", &caps_idx, &temp_ushort) == 2) { | ||
| pp_data->caps[caps_idx].NotRange.StringIndex = temp_ushort; | ||
| continue; | ||
| } | ||
| if (sscanf(line, "pp_data->cap[%d]->NotRange.Reserved2 = %hu\n", &caps_idx, &temp_ushort) == 2) { | ||
| pp_data->caps[caps_idx].NotRange.Reserved2 = temp_ushort; | ||
| continue; | ||
| } | ||
| if (sscanf(line, "pp_data->cap[%d]->NotRange.DesignatorIndex = %hu\n", &caps_idx, &temp_ushort) == 2) { | ||
| pp_data->caps[caps_idx].NotRange.DesignatorIndex = temp_ushort; | ||
| continue; | ||
| } | ||
| if (sscanf(line, "pp_data->cap[%d]->NotRange.Reserved3 = %hu\n", &caps_idx, &temp_ushort) == 2) { | ||
| pp_data->caps[caps_idx].NotRange.Reserved3 = temp_ushort; | ||
| continue; | ||
| } | ||
| if (sscanf(line, "pp_data->cap[%d]->NotRange.DataIndex = %hu\n", &caps_idx, &temp_ushort) == 2) { | ||
| pp_data->caps[caps_idx].NotRange.DataIndex = temp_ushort; | ||
| continue; | ||
| } | ||
| if (sscanf(line, "pp_data->cap[%d]->NotRange.Reserved4 = %hu\n", &caps_idx, &temp_ushort) == 2) { | ||
| pp_data->caps[caps_idx].NotRange.Reserved4 = temp_ushort; | ||
| continue; | ||
| } | ||
| // Button | ||
| if (sscanf(line, "pp_data->cap[%d]->Button.LogicalMin = %ld\n", &caps_idx, &temp_long) == 2) { | ||
| pp_data->caps[caps_idx].Button.LogicalMin = temp_long; | ||
| continue; | ||
| } | ||
| if (sscanf(line, "pp_data->cap[%d]->Button.LogicalMax = %ld\n", &caps_idx, &temp_long) == 2) { | ||
| pp_data->caps[caps_idx].Button.LogicalMax = temp_long; | ||
| continue; | ||
| } | ||
| // NotButton | ||
| if (sscanf(line, "pp_data->cap[%d]->NotButton.HasNull = %hhu\n", &caps_idx, &temp_boolean[0]) == 2) { | ||
| pp_data->caps[caps_idx].NotButton.HasNull = temp_boolean[0]; | ||
| continue; | ||
| } | ||
| if (sscanf(line, "pp_data->cap[%d]->NotButton.Reserved4 = 0x%02hhX%02hhX%02hhX\n", &caps_idx, &temp_uchar[0], &temp_uchar[1], &temp_uchar[2]) == 4) { | ||
| pp_data->caps[caps_idx].NotButton.Reserved4[0] = temp_uchar[0]; | ||
| pp_data->caps[caps_idx].NotButton.Reserved4[1] = temp_uchar[1]; | ||
| pp_data->caps[caps_idx].NotButton.Reserved4[2] = temp_uchar[2]; | ||
| continue; | ||
| } | ||
| if (sscanf(line, "pp_data->cap[%d]->NotButton.LogicalMin = %ld\n", &caps_idx, &temp_long) == 2) { | ||
| pp_data->caps[caps_idx].NotButton.LogicalMin = temp_long; | ||
| continue; | ||
| } | ||
| if (sscanf(line, "pp_data->cap[%d]->NotButton.LogicalMax = %ld\n", &caps_idx, &temp_long) == 2) { | ||
| pp_data->caps[caps_idx].NotButton.LogicalMax = temp_long; | ||
| continue; | ||
| } | ||
| if (sscanf(line, "pp_data->cap[%d]->NotButton.PhysicalMin = %ld\n", &caps_idx, &temp_long) == 2) { | ||
| pp_data->caps[caps_idx].NotButton.PhysicalMin = temp_long; | ||
| continue; | ||
| } | ||
| if (sscanf(line, "pp_data->cap[%d]->NotButton.PhysicalMax = %ld\n", &caps_idx, &temp_long) == 2) { | ||
| pp_data->caps[caps_idx].NotButton.PhysicalMax = temp_long; | ||
| continue; | ||
| } | ||
| if (sscanf(line, "pp_data->cap[%d]->Units = %lu\n", &caps_idx, &temp_ulong) == 2) { | ||
| pp_data->caps[caps_idx].Units = temp_ulong; | ||
| continue; | ||
| } | ||
| if (sscanf(line, "pp_data->cap[%d]->UnitsExp = %lu\n", &caps_idx, &temp_ulong) == 2) { | ||
| pp_data->caps[caps_idx].UnitsExp = temp_ulong; | ||
| continue; | ||
| } | ||
| if (sscanf(line, "pp_data->cap[%d]->Reserved1 = 0x%02hhu%02hhu%02hhu\n", &coll_idx, &temp_uchar[0], &temp_uchar[1], &temp_uchar[2]) == 4) { | ||
| pp_data->caps[caps_idx].Reserved1[0] = temp_uchar[0]; | ||
| pp_data->caps[caps_idx].Reserved1[1] = temp_uchar[1]; | ||
| pp_data->caps[caps_idx].Reserved1[2] = temp_uchar[2]; | ||
| continue; | ||
| } | ||
| fprintf(stderr, "Ignoring unimplemented cap field: %s", line); | ||
| continue; | ||
| } | ||
| if (sscanf(line, "pp_data->LinkCollectionArray[%d]", &coll_idx) == 1) { | ||
| if (pp_data->FirstByteOfLinkCollectionArray == 0 || pp_data->NumberLinkCollectionNodes == 0) { | ||
| fprintf(stderr, "Error reading pp_data file (%s): FirstByteOfLinkCollectionArray or NumberLinkCollectionNodes is 0 or not reported yet\n", line); | ||
| continue; | ||
| } | ||
| if (coll_idx >= pp_data->NumberLinkCollectionNodes) { | ||
| fprintf(stderr, "Error reading pp_data file (%s): the LinkCollection index (%u) is out of boundary (%hu)\n", line, coll_idx, pp_data->NumberLinkCollectionNodes); | ||
| continue; | ||
| } | ||
| phid_pp_link_collection_node pcoll = (phid_pp_link_collection_node)(((unsigned char*)&pp_data->caps[0]) + pp_data->FirstByteOfLinkCollectionArray); | ||
| if (sscanf(line, "pp_data->LinkCollectionArray[%d]->LinkUsage = 0x%04hX\n", &coll_idx, &temp_usage) == 2) { | ||
| pcoll[coll_idx].LinkUsage = temp_usage; | ||
| continue; | ||
| } | ||
| if (sscanf(line, "pp_data->LinkCollectionArray[%d]->LinkUsagePage = 0x%04hX\n", &coll_idx, &temp_usage) == 2) { | ||
| pcoll[coll_idx].LinkUsagePage = temp_usage; | ||
| continue; | ||
| } | ||
| if (sscanf(line, "pp_data->LinkCollectionArray[%d]->Parent = %hu\n", &coll_idx, &temp_ushort) == 2) { | ||
| pcoll[coll_idx].Parent = temp_ushort; | ||
| continue; | ||
| } | ||
| if (sscanf(line, "pp_data->LinkCollectionArray[%d]->NumberOfChildren = %hu\n", &coll_idx, &temp_ushort) == 2) { | ||
| pcoll[coll_idx].NumberOfChildren = temp_ushort; | ||
| continue; | ||
| } | ||
| if (sscanf(line, "pp_data->LinkCollectionArray[%d]->NextSibling = %hu\n", &coll_idx, &temp_ushort) == 2) { | ||
| pcoll[coll_idx].NextSibling = temp_ushort; | ||
| continue; | ||
| } | ||
| if (sscanf(line, "pp_data->LinkCollectionArray[%d]->FirstChild = %hu\n", &coll_idx, &temp_ushort) == 2) { | ||
| pcoll[coll_idx].FirstChild = temp_ushort; | ||
| continue; | ||
| } | ||
| if (sscanf(line, "pp_data->LinkCollectionArray[%d]->CollectionType = %ld\n", &coll_idx, &temp_ulong) == 2) { | ||
| pcoll[coll_idx].CollectionType = temp_ulong; | ||
| continue; | ||
| } | ||
| if (sscanf(line, "pp_data->LinkCollectionArray[%d]->IsAlias = %ld\n", &coll_idx, &temp_ulong) == 2) { | ||
| pcoll[coll_idx].IsAlias = temp_ulong; | ||
| continue; | ||
| } | ||
| if (sscanf(line, "pp_data->LinkCollectionArray[%d]->Reserved = %ld\n", &coll_idx, &temp_ulong) == 2) { | ||
| pcoll[coll_idx].Reserved = temp_ulong; | ||
| continue; | ||
| } | ||
| fprintf(stderr, "Ignoring unimplemented LinkCollectionArray field: %s", line); | ||
| continue; | ||
| } | ||
| } | ||
| //end: | ||
| fclose(file); | ||
| if (pp_data == &static_pp_data) { | ||
| return NULL; | ||
| } | ||
| return pp_data; | ||
| } | ||
| static BOOLEAN read_hex_data_from_text_file(const char *filename, unsigned char *data_out, size_t data_size, size_t *actual_read) | ||
| { | ||
| size_t read_index = 0; | ||
| FILE* file = NULL; | ||
| errno_t err = fopen_s(&file, filename, "r"); | ||
| if (err != 0) { | ||
| fprintf(stderr, "ERROR: Couldn't open file '%s' for reading: %s\n", filename, strerror(err)); | ||
| return FALSE; | ||
| } | ||
| BOOLEAN result = TRUE; | ||
| unsigned int val; | ||
| char buf[16]; | ||
| while (fscanf(file, "%15s", buf) == 1) { | ||
| if (sscanf(buf, "0x%X", &val) != 1) { | ||
| fprintf(stderr, "Invalid HEX text ('%s') file, got %s\n", filename, buf); | ||
| result = FALSE; | ||
| goto end; | ||
| } | ||
| if (read_index >= data_size) { | ||
| fprintf(stderr, "Buffer for file read is too small. Got only %zu bytes to read '%s'\n", data_size, filename); | ||
| result = FALSE; | ||
| goto end; | ||
| } | ||
| if (val > (unsigned char)-1) { | ||
| fprintf(stderr, "Invalid HEX text ('%s') file, got a value of: %u\n", filename, val); | ||
| result = FALSE; | ||
| goto end; | ||
| } | ||
| data_out[read_index] = (unsigned char) val; | ||
| read_index++; | ||
| } | ||
| if (!feof(file)) { | ||
| fprintf(stderr, "Invalid HEX text ('%s') file - failed to read all values\n", filename); | ||
| result = FALSE; | ||
| goto end; | ||
| } | ||
| *actual_read = read_index; | ||
| end: | ||
| fclose(file); | ||
| return result; | ||
| } | ||
| int main(int argc, char* argv[]) | ||
| { | ||
| if (argc != 3) { | ||
| fprintf(stderr, "Expected 2 arguments for the test ('<>.pp_data' and '<>_expected.rpt_desc'), got: %d\n", argc - 1); | ||
| return EXIT_FAILURE; | ||
| } | ||
| printf("Checking: '%s' / '%s'\n", argv[1], argv[2]); | ||
| hidp_preparsed_data *pp_data = alloc_preparsed_data_from_file(argv[1]); | ||
| if (pp_data == NULL) { | ||
| return EXIT_FAILURE; | ||
| } | ||
| int result = EXIT_SUCCESS; | ||
| unsigned char report_descriptor[HID_API_MAX_REPORT_DESCRIPTOR_SIZE]; | ||
| int res = hid_winapi_descriptor_reconstruct_pp_data(pp_data, report_descriptor, sizeof(report_descriptor)); | ||
| if (res < 0) { | ||
| result = EXIT_FAILURE; | ||
| fprintf(stderr, "Failed to reconstruct descriptor"); | ||
| goto end; | ||
| } | ||
| size_t report_descriptor_size = (size_t) res; | ||
| unsigned char expected_report_descriptor[HID_API_MAX_REPORT_DESCRIPTOR_SIZE]; | ||
| size_t expected_report_descriptor_size = 0; | ||
| if (!read_hex_data_from_text_file(argv[2], expected_report_descriptor, sizeof(expected_report_descriptor), &expected_report_descriptor_size)) { | ||
| result = EXIT_FAILURE; | ||
| goto end; | ||
| } | ||
| if (report_descriptor_size == expected_report_descriptor_size) { | ||
| if (memcmp(report_descriptor, expected_report_descriptor, report_descriptor_size) == 0) { | ||
| printf("Reconstructed Report Descriptor matches the expected descriptor\n"); | ||
| goto end; | ||
| } | ||
| else { | ||
| result = EXIT_FAILURE; | ||
| fprintf(stderr, "Reconstructed Report Descriptor has different content than expected\n"); | ||
| } | ||
| } | ||
| else { | ||
| result = EXIT_FAILURE; | ||
| fprintf(stderr, "Reconstructed Report Descriptor has different size: %zu when expected %zu\n", report_descriptor_size, expected_report_descriptor_size); | ||
| } | ||
| printf(" Reconstructed Report Descriptor:\n"); | ||
| for (int i = 0; i < res; i++) { | ||
| printf("0x%02X, ", report_descriptor[i]); | ||
| } | ||
| printf("\n"); | ||
| fflush(stdout); | ||
| end: | ||
| free(pp_data); | ||
| return result; | ||
| } |
| /**************************************************************************** | ||
| Copyright (c) 2015 Artyom Pavlov All Rights Reserved. | ||
| This file is part of hidapi-rs, based on hidapi_rust by Roland Ruckerbauer. | ||
| It's also based on the Oleg Bulatov's work (https://github.com/dmage/co2mon) | ||
| ****************************************************************************/ | ||
| //! Opens a KIT MT 8057 CO2 detector and reads data from it. This | ||
| //! example will not work unless such device is plugged into your system. | ||
| use hidapi::{HidApi, HidError}; | ||
| use std::io; | ||
| const CODE_TEMPERATURE: u8 = 0x42; | ||
| const CODE_CONCENTRATION: u8 = 0x50; | ||
| const HID_TIMEOUT: i32 = 5000; | ||
| const DEV_VID: u16 = 0x04d9; | ||
| const DEV_PID: u16 = 0xa052; | ||
| const PACKET_SIZE: usize = 8; | ||
| type Packet = [u8; PACKET_SIZE]; | ||
| enum CO2Result { | ||
| Temperature(f32), | ||
| Concentration(u16), | ||
| Unknown(u8, u16), | ||
| Error(&'static str), | ||
| } | ||
| fn decode_temperature(value: u16) -> f32 { | ||
| (value as f32) * 0.0625 - 273.15 | ||
| } | ||
| fn decrypt(buf: Packet) -> Packet { | ||
| let mut res: [u8; PACKET_SIZE] = [ | ||
| (buf[3] << 5) | (buf[2] >> 3), | ||
| (buf[2] << 5) | (buf[4] >> 3), | ||
| (buf[4] << 5) | (buf[0] >> 3), | ||
| (buf[0] << 5) | (buf[7] >> 3), | ||
| (buf[7] << 5) | (buf[1] >> 3), | ||
| (buf[1] << 5) | (buf[6] >> 3), | ||
| (buf[6] << 5) | (buf[5] >> 3), | ||
| (buf[5] << 5) | (buf[3] >> 3), | ||
| ]; | ||
| let magic_word = b"Htemp99e"; | ||
| for i in 0..PACKET_SIZE { | ||
| let sub_val: u8 = (magic_word[i] << 4) | (magic_word[i] >> 4); | ||
| res[i] = u8::wrapping_sub(res[i], sub_val); | ||
| } | ||
| res | ||
| } | ||
| fn decode_buf(buf: Packet) -> CO2Result { | ||
| // Do we need to decrypt the data? | ||
| let res = if buf[4] == 0x0d { buf } else { decrypt(buf) }; | ||
| let kind = res[0]; | ||
| let val = u16::from_be_bytes(res[1..3].try_into().unwrap()); | ||
| let checksum = res[3]; | ||
| let tail = res[4]; | ||
| if tail != 0x0d { | ||
| return CO2Result::Error("Unexpected data (data[4] != 0x0d)"); | ||
| } | ||
| let checksum_calc = res[0].wrapping_add(res[1]).wrapping_add(res[2]); | ||
| if checksum != checksum_calc { | ||
| return CO2Result::Error("Checksum error"); | ||
| } | ||
| match kind { | ||
| CODE_TEMPERATURE => CO2Result::Temperature(decode_temperature(val)), | ||
| CODE_CONCENTRATION => { | ||
| if val > 3000 { | ||
| CO2Result::Error("Concentration bigger than 3000 (uninitialized device?)") | ||
| } else { | ||
| CO2Result::Concentration(val) | ||
| } | ||
| } | ||
| _ => CO2Result::Unknown(res[0], val), | ||
| } | ||
| } | ||
| fn invalid_data_err(msg: impl Into<String>) -> HidError { | ||
| HidError::IoError { | ||
| error: io::Error::new(io::ErrorKind::InvalidData, msg.into()), | ||
| } | ||
| } | ||
| fn main() -> Result<(), HidError> { | ||
| let api = HidApi::new()?; | ||
| let dev = api.open(DEV_VID, DEV_PID)?; | ||
| dev.send_feature_report(&[0; PACKET_SIZE])?; | ||
| if let Some(manufacturer) = dev.get_manufacturer_string()? { | ||
| println!("Manufacurer:\t{manufacturer}"); | ||
| } | ||
| if let Some(product) = dev.get_product_string()? { | ||
| println!("Product:\t{product}"); | ||
| } | ||
| if let Some(serial_number) = dev.get_serial_number_string()? { | ||
| println!("Serial number:\t{serial_number}"); | ||
| } | ||
| let mut buf = [0; PACKET_SIZE]; | ||
| loop { | ||
| let n = dev.read_timeout(&mut buf[..], HID_TIMEOUT)?; | ||
| if n != PACKET_SIZE { | ||
| let msg = format!("unexpected packet length: {n}/{PACKET_SIZE}"); | ||
| return Err(invalid_data_err(msg)); | ||
| } | ||
| match decode_buf(buf) { | ||
| CO2Result::Temperature(val) => println!("Temp:\t{val}"), | ||
| CO2Result::Concentration(val) => println!("Conc:\t{val}"), | ||
| CO2Result::Unknown(..) => (), | ||
| CO2Result::Error(msg) => { | ||
| return Err(invalid_data_err(msg)); | ||
| } | ||
| } | ||
| } | ||
| } |
| use hidapi::HidApi; | ||
| fn main() { | ||
| println!("Printing all available hid devices:"); | ||
| match HidApi::new() { | ||
| Ok(api) => { | ||
| for device in api.device_list() { | ||
| println!( | ||
| " {} (Interface {}):", | ||
| match device.product_string() { | ||
| Some(s) => s, | ||
| _ => "<COULD NOT FETCH>", | ||
| }, | ||
| device.interface_number() | ||
| ); | ||
| let mut descriptor = vec![0u8; 2048]; | ||
| match device | ||
| .open_device(&api) | ||
| .and_then(|dev| dev.get_report_descriptor(&mut descriptor)) | ||
| { | ||
| Ok(length) => println!(" {:?}", &mut descriptor[..length]), | ||
| Err(err) => println!(" Failed to retrieve descriptor ({:?})", err), | ||
| } | ||
| } | ||
| } | ||
| Err(e) => { | ||
| eprintln!("Error: {}", e); | ||
| } | ||
| } | ||
| } |
| /**************************************************************************** | ||
| Copyright (c) 2022 ruabmbua All Rights Reserved. | ||
| ****************************************************************************/ | ||
| //! Sets the sidechannel volume of the logitech gpro x headset | ||
| extern crate hidapi; | ||
| use hidapi::HidApi; | ||
| use std::error::Error; | ||
| fn main() -> Result<(), Box<dyn Error>> { | ||
| let vol = std::env::args() | ||
| .nth(1) | ||
| .map(|arg| arg.parse::<u8>()) | ||
| .ok_or("missing sidechannel volume arg")?? | ||
| .min(100); | ||
| let api = HidApi::new()?; | ||
| let dev = api.open(0x046d, 0x0aaa)?; | ||
| println!("Setting sidechannel volume to {}", vol); | ||
| dev.write(&[0x11, 0xff, 0x05, 0x1c, vol])?; | ||
| Ok(()) | ||
| } |
| /**************************************************************************** | ||
| Copyright (c) 2015 Osspial All Rights Reserved. | ||
| This file is part of hidapi-rs, based on hidapi_rust by Roland Ruckerbauer. | ||
| ****************************************************************************/ | ||
| //! Prints out a list of HID devices | ||
| extern crate hidapi; | ||
| use hidapi::HidApi; | ||
| fn main() { | ||
| println!("Printing all available hid devices:"); | ||
| match HidApi::new() { | ||
| Ok(api) => { | ||
| for device in api.device_list() { | ||
| println!( | ||
| "VID: {:04x}, PID: {:04x}, Serial: {}, Product name: {}, Interface: {}", | ||
| device.vendor_id(), | ||
| device.product_id(), | ||
| match device.serial_number() { | ||
| Some(s) => s, | ||
| _ => "<COULD NOT FETCH>", | ||
| }, | ||
| match device.product_string() { | ||
| Some(s) => s, | ||
| _ => "<COULD NOT FETCH>", | ||
| }, | ||
| device.interface_number() | ||
| ); | ||
| } | ||
| } | ||
| Err(e) => { | ||
| eprintln!("Error: {}", e); | ||
| } | ||
| } | ||
| } |
| /**************************************************************************** | ||
| Copyright (c) 2018 Roland Ruckerbauer All Rights Reserved. | ||
| This file is part of hidapi-rs, based on hidapi-rs by Osspial | ||
| ****************************************************************************/ | ||
| //! Opens the first hid device it can find, and reads data in a blocking fashion | ||
| //! from it in an endless loop. | ||
| extern crate hidapi; | ||
| use hidapi::{HidApi, HidError}; | ||
| fn main() { | ||
| fn run() -> Result<(), HidError> { | ||
| let hidapi = HidApi::new()?; | ||
| let device_info = hidapi | ||
| .device_list() | ||
| .next() | ||
| .expect("No devices are available!") | ||
| .clone(); | ||
| println!( | ||
| "Opening device:\n VID: {:04x}, PID: {:04x}\n", | ||
| device_info.vendor_id(), | ||
| device_info.product_id() | ||
| ); | ||
| let device = device_info.open_device(&hidapi)?; | ||
| let mut buf = vec![0; 64]; | ||
| println!("Reading data from device ...\n"); | ||
| loop { | ||
| let len = device.read(&mut buf)?; | ||
| println!("{:?}", &buf[..len]); | ||
| } | ||
| } | ||
| if let Err(e) = run() { | ||
| eprintln!("Error: {}", e); | ||
| } | ||
| } |
| /**************************************************************************** | ||
| Copyright (c) 2015 Osspial All Rights Reserved. | ||
| This file is part of hidapi-rs, based on hidapi_rust by Roland Ruckerbauer. | ||
| ****************************************************************************/ | ||
| //! Opens a Thrustmaster T-Flight HOTAS X HID and reads data from it. This | ||
| //! example will not work unless such an HID is plugged in to your system. | ||
| //! Will update in the future to support all HIDs. | ||
| extern crate hidapi; | ||
| use hidapi::HidApi; | ||
| fn main() { | ||
| let api = HidApi::new().expect("Failed to create API instance"); | ||
| let joystick = api.open(1103, 45320).expect("Failed to open device"); | ||
| loop { | ||
| let mut buf = [0u8; 256]; | ||
| let res = joystick.read(&mut buf[..]).unwrap(); | ||
| let mut data_string = String::new(); | ||
| for u in &buf[..res] { | ||
| data_string.push_str(&(u.to_string() + "\t")); | ||
| } | ||
| println!("{}", data_string); | ||
| } | ||
| } |
| /**************************************************************************** | ||
| Copyright (c) 2015 Osspial All Rights Reserved. | ||
| This file is part of hidapi-rs, based on hidapi_rust by Roland Ruckerbauer. | ||
| ****************************************************************************/ | ||
| //! This example shows the added possibility (after version 0.4.1), | ||
| //! to move devices into a function / or closure with static lifetime bounds. | ||
| extern crate hidapi; | ||
| use hidapi::{HidApi, HidDevice}; | ||
| use std::rc::Rc; | ||
| fn main() { | ||
| let _dev = test_lt(); | ||
| } | ||
| fn requires_static_lt_bound<F: Fn() + 'static>(f: F) { | ||
| f(); | ||
| } | ||
| fn test_lt() -> Rc<HidDevice> { | ||
| let api = HidApi::new().expect("Hidapi init failed"); | ||
| let mut devices = api.device_list(); | ||
| let dev_info = devices | ||
| .next() | ||
| .expect("There is not a single hid device available"); | ||
| let dev = Rc::new( | ||
| api.open(dev_info.vendor_id(), dev_info.product_id()) | ||
| .expect("Can not open device"), | ||
| ); | ||
| let dev_1 = dev.clone(); | ||
| requires_static_lt_bound(move || { | ||
| println!("{:?}", dev_1.get_device_info().unwrap()); //<! Can be captured by closure with static lt | ||
| }); | ||
| dev //<! Can be returned from a function, which exceeds the lifetime of the API context | ||
| } |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet