Browse Source

Updated hidapi to 0.14.0 release

Upstream: https://github.com/libusb/hidapi/releases/tag/hidapi-0.14.0
Sam Lantinga 2 years ago
parent
commit
3b7b8f3c09
100 changed files with 8295 additions and 6094 deletions
  1. 31 0
      src/hidapi/.appveyor.yml
  2. 34 0
      src/hidapi/.builds/freebsd.yml
  3. 18 0
      src/hidapi/.builds/netbsd.yml
  4. 19 0
      src/hidapi/.builds/openbsd.yml
  5. 33 0
      src/hidapi/.cirrus.yml
  6. 7 0
      src/hidapi/.gitattributes
  7. 540 0
      src/hidapi/.github/workflows/builds.yml
  8. 196 0
      src/hidapi/.github/workflows/checks.yml
  9. 58 0
      src/hidapi/.github/workflows/docs.yaml
  10. 32 0
      src/hidapi/.gitignore
  11. 3 1
      src/hidapi/AUTHORS.txt
  12. 114 0
      src/hidapi/BUILD.autotools.md
  13. 280 0
      src/hidapi/BUILD.cmake.md
  14. 127 0
      src/hidapi/BUILD.md
  15. 105 0
      src/hidapi/CMakeLists.txt
  16. 15 11
      src/hidapi/HACKING.txt
  17. 5 5
      src/hidapi/Makefile.am
  18. 196 0
      src/hidapi/README.md
  19. 0 339
      src/hidapi/README.txt
  20. 1 0
      src/hidapi/VERSION
  21. 0 1443
      src/hidapi/android/hid.cpp
  22. 0 39
      src/hidapi/android/hid.h
  23. 0 16
      src/hidapi/android/jni/Android.mk
  24. 0 2
      src/hidapi/android/jni/Application.mk
  25. 0 14
      src/hidapi/android/project.properties
  26. 44 24
      src/hidapi/configure.ac
  27. 31 0
      src/hidapi/dist/hidapi.podspec
  28. BIN
      src/hidapi/documentation/cmake-gui-drop-down.png
  29. BIN
      src/hidapi/documentation/cmake-gui-highlights.png
  30. 682 327
      src/hidapi/doxygen/Doxyfile
  31. 13 0
      src/hidapi/doxygen/main_page.md
  32. 261 58
      src/hidapi/hidapi/hidapi.h
  33. 17 0
      src/hidapi/hidtest/.gitignore
  34. 40 0
      src/hidapi/hidtest/CMakeLists.txt
  35. 11 3
      src/hidapi/hidtest/Makefile.am
  36. 0 194
      src/hidapi/hidtest/hidtest.cpp
  37. 316 0
      src/hidapi/hidtest/test.c
  38. 0 32
      src/hidapi/ios/Makefile-manual
  39. 0 9
      src/hidapi/ios/Makefile.am
  40. 0 996
      src/hidapi/ios/hid.m
  41. 8 0
      src/hidapi/libusb/.gitignore
  42. 107 0
      src/hidapi/libusb/CMakeLists.txt
  43. 4 0
      src/hidapi/libusb/Makefile-manual
  44. 8 1
      src/hidapi/libusb/Makefile.am
  45. 4 11
      src/hidapi/libusb/Makefile.freebsd
  46. 39 0
      src/hidapi/libusb/Makefile.haiku
  47. 5 12
      src/hidapi/libusb/Makefile.linux
  48. 357 451
      src/hidapi/libusb/hid.c
  49. 56 0
      src/hidapi/libusb/hidapi_libusb.h
  50. 0 3
      src/hidapi/libusb/hidusb.cpp
  51. 18 0
      src/hidapi/linux/.gitignore
  52. 38 0
      src/hidapi/linux/CMakeLists.txt
  53. 5 12
      src/hidapi/linux/Makefile-manual
  54. 0 59
      src/hidapi/linux/README.txt
  55. 631 263
      src/hidapi/linux/hid.c
  56. 0 3
      src/hidapi/linux/hidraw.cpp
  57. 5 0
      src/hidapi/m4/.gitignore
  58. 17 0
      src/hidapi/mac/.gitignore
  59. 48 0
      src/hidapi/mac/CMakeLists.txt
  60. 6 11
      src/hidapi/mac/Makefile-manual
  61. 504 345
      src/hidapi/mac/hid.c
  62. 98 0
      src/hidapi/mac/hidapi_darwin.h
  63. 22 0
      src/hidapi/meson.build
  64. 1 0
      src/hidapi/pc/.gitignore
  65. 1 0
      src/hidapi/pc/hidapi-hidraw.pc.in
  66. 1 0
      src/hidapi/pc/hidapi-libusb.pc.in
  67. 1 0
      src/hidapi/pc/hidapi.pc.in
  68. 193 0
      src/hidapi/src/CMakeLists.txt
  69. 61 0
      src/hidapi/src/cmake/hidapi-config.cmake.in
  70. 2 0
      src/hidapi/subprojects/README.md
  71. 10 0
      src/hidapi/subprojects/hidapi_build_cmake/CMakeLists.txt
  72. 20 0
      src/hidapi/testgui/.gitignore
  73. 1 1
      src/hidapi/testgui/Makefile.mingw
  74. 2 1
      src/hidapi/testgui/copy_to_bundle.sh
  75. 0 134
      src/hidapi/testgui/mac_support.cpp
  76. 11 2
      src/hidapi/testgui/mac_support_cocoa.m
  77. 0 2
      src/hidapi/testgui/start.sh
  78. 20 20
      src/hidapi/testgui/testgui.sln
  79. 217 217
      src/hidapi/testgui/testgui.vcproj
  80. 36 0
      src/hidapi/udev/69-hid.rules
  81. 0 33
      src/hidapi/udev/99-hid.rules
  82. 17 0
      src/hidapi/windows/.gitignore
  83. 63 0
      src/hidapi/windows/CMakeLists.txt
  84. 0 1
      src/hidapi/windows/Makefile.am
  85. 6 11
      src/hidapi/windows/Makefile.mingw
  86. 0 17
      src/hidapi/windows/ddk_build/hidapi.def
  87. 0 49
      src/hidapi/windows/ddk_build/makefile
  88. 0 23
      src/hidapi/windows/ddk_build/sources
  89. 429 456
      src/hidapi/windows/hid.c
  90. 35 0
      src/hidapi/windows/hidapi.rc
  91. 41 29
      src/hidapi/windows/hidapi.sln
  92. 200 201
      src/hidapi/windows/hidapi.vcproj
  93. 200 0
      src/hidapi/windows/hidapi.vcxproj
  94. 14 16
      src/hidapi/windows/hidapi_cfgmgr32.h
  95. 987 0
      src/hidapi/windows/hidapi_descriptor_reconstruct.c
  96. 238 0
      src/hidapi/windows/hidapi_descriptor_reconstruct.h
  97. 7 0
      src/hidapi/windows/hidapi_hidpi.h
  98. 2 1
      src/hidapi/windows/hidapi_hidsdi.h
  99. 74 0
      src/hidapi/windows/hidapi_winapi.h
  100. 196 196
      src/hidapi/windows/hidtest.vcproj

+ 31 - 0
src/hidapi/.appveyor.yml

@@ -0,0 +1,31 @@
+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"

+ 34 - 0
src/hidapi/.builds/freebsd.yml

@@ -0,0 +1,34 @@
+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

+ 18 - 0
src/hidapi/.builds/netbsd.yml

@@ -0,0 +1,18 @@
+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

+ 19 - 0
src/hidapi/.builds/openbsd.yml

@@ -0,0 +1,19 @@
+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

+ 33 - 0
src/hidapi/.cirrus.yml

@@ -0,0 +1,33 @@
+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

+ 7 - 0
src/hidapi/.gitattributes

@@ -0,0 +1,7 @@
+* text=auto
+
+*.sln text eol=crlf
+*.vcproj text eol=crlf
+
+bootstrap text eol=lf
+configure.ac text eol=lf

+ 540 - 0
src/hidapi/.github/workflows/builds.yml

@@ -0,0 +1,540 @@
+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
+      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/[email protected]
+    - 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

+ 196 - 0
src/hidapi/.github/workflows/checks.yml

@@ -0,0 +1,196 @@
+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/[email protected]
+      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 [email protected] \
+          --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

+ 58 - 0
src/hidapi/.github/workflows/docs.yaml

@@ -0,0 +1,58 @@
+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

+ 32 - 0
src/hidapi/.gitignore

@@ -0,0 +1,32 @@
+
+# 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/

+ 3 - 1
src/hidapi/AUTHORS.txt

@@ -10,7 +10,9 @@ Ludovic Rousseau <[email protected]>:
 	Bug fixes
 	Correctness fixes
 
+libusb/hidapi Team:
+	Development/maintainance since June 4th 2019
 
 For a comprehensive list of contributions, see the commit list at github:
-	https://github.com/libusb/hidapi/commits/master
+	https://github.com/libusb/hidapi/graphs/contributors
 

+ 114 - 0
src/hidapi/BUILD.autotools.md

@@ -0,0 +1,114 @@
+# 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
+```

+ 280 - 0
src/hidapi/BUILD.cmake.md

@@ -0,0 +1,280 @@
+# 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 prefered 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.:
+![an example of highlighted variables in cmake-gui](documentation/cmake-gui-highlights.png "cmake-gui highlighted variables")<br>
+E.g.2:<br>
+![an example of drop-down menu in cmake-gui](documentation/cmake-gui-drop-down.png "cmake-gui drop-down menu")<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"
+
+```

+ 127 - 0
src/hidapi/BUILD.md

@@ -0,0 +1,127 @@
+# 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.

+ 105 - 0
src/hidapi/CMakeLists.txt

@@ -0,0 +1,105 @@
+cmake_minimum_required(VERSION 3.1.3 FATAL_ERROR)
+
+if(NOT CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR)
+    add_subdirectory(src)
+    # compatibility with find_package() vs add_subdirectory
+    set(hidapi_VERSION "${hidapi_VERSION}" PARENT_SCOPE)
+    return()
+endif()
+# All of the below in this file is meant for a standalone build.
+# When building as a subdirectory of a larger project, most of the options may not make sense for it,
+# so it is up to developer to configure those, e.g.:
+#
+# # a subfolder of a master project, e.g.: 3rdparty/hidapi/CMakeLists.txt
+#
+# set(HIDAPI_WITH_HIDRAW OFF)
+# set(CMAKE_FRAMEWORK ON)
+# # and keep everything else to their defaults
+# add_subdirectory(hidapi)
+#
+
+set(DEFAULT_CMAKE_BUILD_TYPES "Debug" "Release" "MinSizeRel" "RelWithDebInfo")
+if(NOT DEFINED CMAKE_BUILD_TYPE OR NOT CMAKE_BUILD_TYPE)
+    set(CMAKE_BUILD_TYPE "Release" CACHE STRING "${DEFAULT_CMAKE_BUILD_TYPES}" FORCE)
+endif()
+# This part is for convenience, when used one of the standard build types with cmake-gui
+list(FIND DEFAULT_CMAKE_BUILD_TYPES "${CMAKE_BUILD_TYPE}" _build_type_index)
+if(${_build_type_index} GREATER -1)
+    # set it optionally, so a custom CMAKE_BUILD_TYPE can be used as well, if needed
+    set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS ${DEFAULT_CMAKE_BUILD_TYPES})
+endif()
+unset(_build_type_index)
+#
+
+project(hidapi LANGUAGES C)
+
+if(APPLE)
+    if(NOT CMAKE_VERSION VERSION_LESS "3.15")
+        option(CMAKE_FRAMEWORK "Build macOS/iOS Framework version of the library" OFF)
+    endif()
+elseif(NOT WIN32)
+    if(CMAKE_SYSTEM_NAME MATCHES "Linux")
+        option(HIDAPI_WITH_HIDRAW "Build HIDRAW-based implementation of HIDAPI" ON)
+        option(HIDAPI_WITH_LIBUSB "Build LIBUSB-based implementation of HIDAPI" ON)
+    endif()
+endif()
+
+option(BUILD_SHARED_LIBS "Build shared version of the libraries, otherwise build statically" ON)
+
+set(HIDAPI_INSTALL_TARGETS ON)
+set(HIDAPI_PRINT_VERSION ON)
+
+set(IS_DEBUG_BUILD OFF)
+if(CMAKE_BUILD_TYPE STREQUAL "Debug")
+    set(IS_DEBUG_BUILD ON)
+endif()
+
+option(HIDAPI_ENABLE_ASAN "Build HIDAPI with ASAN address sanitizer instrumentation" OFF)
+
+if(HIDAPI_ENABLE_ASAN)
+    set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=address")
+    if(MSVC)
+        # the default is to have "/INCREMENTAL" which causes a warning when "-fsanitize=address" is present
+        set(CMAKE_EXE_LINKER_FLAGS_DEBUG "${CMAKE_EXE_LINKER_FLAGS_DEBUG} /INCREMENTAL:NO")
+        set(CMAKE_SHARED_LINKER_FLAGS_DEBUG "${CMAKE_SHARED_LINKER_FLAGS_DEBUG} /INCREMENTAL:NO")
+        set(CMAKE_EXE_LINKER_FLAGS_RELWITHDEBINFO "${CMAKE_EXE_LINKER_FLAGS_RELWITHDEBINFO} /INCREMENTAL:NO")
+        set(CMAKE_SHARED_LINKER_FLAGS_RELWITHDEBINFO "${CMAKE_SHARED_LINKER_FLAGS_RELWITHDEBINFO} /INCREMENTAL:NO")
+    endif()
+endif()
+
+if(WIN32)
+    # so far only Windows has tests
+    option(HIDAPI_WITH_TESTS "Build HIDAPI (unit-)tests" ${IS_DEBUG_BUILD})
+else()
+    set(HIDAPI_WITH_TESTS OFF)
+endif()
+
+if(HIDAPI_WITH_TESTS)
+    enable_testing()
+endif()
+
+if(WIN32)
+    option(HIDAPI_BUILD_PP_DATA_DUMP "Build small Windows console application pp_data_dump.exe" ${IS_DEBUG_BUILD})
+endif()
+
+add_subdirectory(src)
+
+option(HIDAPI_BUILD_HIDTEST "Build small console test application hidtest" ${IS_DEBUG_BUILD})
+if(HIDAPI_BUILD_HIDTEST)
+    add_subdirectory(hidtest)
+endif()
+
+if(HIDAPI_ENABLE_ASAN)
+    if(NOT MSVC)
+        # MSVC doesn't recognize those options, other compilers - requiring it
+        foreach(HIDAPI_TARGET hidapi_winapi hidapi_darwin hidapi_hidraw hidapi_libusb hidtest_hidraw hidtest_libusb hidtest)
+            if(TARGET ${HIDAPI_TARGET})
+                if(BUILD_SHARED_LIBS)
+                    target_link_options(${HIDAPI_TARGET} PRIVATE -fsanitize=address)
+                else()
+                    target_link_options(${HIDAPI_TARGET} PUBLIC -fsanitize=address)
+                endif()
+            endif()
+        endforeach()
+    endif()
+endif()

+ 15 - 11
src/hidapi/HACKING.txt

@@ -1,15 +1,19 @@
 This file is mostly for the maintainer.
 
-1. Build hidapi.dll
-2. Build hidtest.exe in DEBUG and RELEASE
-3. Commit all
+Updating a Version:
+1. Update VERSION file.
+2. HID_API_VERSION_MAJOR/HID_API_VERSION_MINOR/HID_API_VERSION_PATCH in hidapi.h.
 
-4. Run the Following
-	export VERSION=0.1.0
-	export TAG_NAME=hidapi-$VERSION
-	git tag $TAG_NAME
-	git archive --format zip --prefix $TAG_NAME/ $TAG_NAME >../$TAG_NAME.zip
-5. Test the zip file.
-6. Run the following:
-	git push origin $TAG_NAME
+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.

+ 5 - 5
src/hidapi/Makefile.am

@@ -23,10 +23,6 @@ if OS_DARWIN
 SUBDIRS += mac
 endif
 
-if OS_IOS
-SUBDIRS += ios
-endif
-
 if OS_FREEBSD
 SUBDIRS += libusb
 endif
@@ -35,6 +31,10 @@ if OS_KFREEBSD
 SUBDIRS += libusb
 endif
 
+if OS_HAIKU
+SUBDIRS += libusb
+endif
+
 if OS_WINDOWS
 SUBDIRS += windows
 endif
@@ -48,7 +48,7 @@ endif
 EXTRA_DIST = udev doxygen
 
 dist_doc_DATA = \
- README.txt \
+ README.md \
  AUTHORS.txt \
  LICENSE-bsd.txt \
  LICENSE-gpl3.txt \

+ 196 - 0
src/hidapi/README.md

@@ -0,0 +1,196 @@
+## HIDAPI library for Windows, Linux, FreeBSD and macOS
+
+| CI instance          | Status |
+|----------------------|--------|
+| `Linux/macOS/Windows (master)` | [![GitHub Builds](https://github.com/libusb/hidapi/workflows/GitHub%20Builds/badge.svg?branch=master)](https://github.com/libusb/hidapi/actions/workflows/builds.yml?query=branch%3Amaster) |
+| `Windows (master)` | [![Build status](https://ci.appveyor.com/api/projects/status/xfmr5fo8w0re8ded/branch/master?svg=true)](https://ci.appveyor.com/project/libusb/hidapi/branch/master) |
+| `BSD, last build (branch/PR)` | [![builds.sr.ht status](https://builds.sr.ht/~z3ntu/hidapi.svg)](https://builds.sr.ht/~z3ntu/hidapi) |
+| `Coverity Scan (last)` | ![Coverity Scan](https://scan.coverity.com/projects/583/badge.svg) |
+
+HIDAPI is a multi-platform library which allows an application to interface
+with USB and Bluetooth HID-Class devices on Windows, Linux, FreeBSD, and macOS.
+HIDAPI can be either built as a shared library (`.so`, `.dll` or `.dylib`) or
+can be embedded directly into a target application by adding a _single source_
+file (per platform) and a single header.<br>
+See [remarks](BUILD.md#embedding-hidapi-directly-into-your-source-tree) on embedding _directly_ into your build system.
+
+HIDAPI library was originally developed by Alan Ott ([signal11](https://github.com/signal11)).
+
+It was moved to [libusb/hidapi](https://github.com/libusb/hidapi) on June 4th, 2019, in order to merge important bugfixes and continue development of the library.
+
+## Table of Contents
+
+* [About](#about)
+    * [Test GUI](#test-gui)
+    * [Console Test App](#console-test-app)
+* [What Does the API Look Like?](#what-does-the-api-look-like)
+* [License](#license)
+* [Installing HIDAPI](#installing-hidapi)
+* [Build from Source](#build-from-source)
+
+
+## About
+
+### HIDAPI has four back-ends:
+* Windows (using `hid.dll`)
+* Linux/hidraw (using the Kernel's hidraw driver)
+* libusb (using libusb-1.0 - Linux/BSD/other UNIX-like systems)
+* macOS (using IOHidManager)
+
+On Linux, either the hidraw or the libusb back-end can be used. There are
+tradeoffs, and the functionality supported is slightly different. Both are
+built by default. It is up to the application linking to hidapi to choose
+the backend at link time by linking to either `libhidapi-libusb` or
+`libhidapi-hidraw`.
+
+Note that you will need to install an udev rule file with your application
+for unprivileged users to be able to access HID devices with hidapi. Refer
+to the [69-hid.rules](udev/69-hid.rules) file in the `udev` directory
+for an example.
+
+#### __Linux/hidraw__ (`linux/hid.c`):
+
+This back-end uses the hidraw interface in the Linux kernel, and supports
+both USB and Bluetooth HID devices. It requires kernel version at least 2.6.39
+to build. In addition, it will only communicate with devices which have hidraw
+nodes associated with them.
+Keyboards, mice, and some other devices which are blacklisted from having
+hidraw nodes will not work. Fortunately, for nearly all the uses of hidraw,
+this is not a problem.
+
+#### __Linux/FreeBSD/libusb__ (`libusb/hid.c`):
+
+This back-end uses libusb-1.0 to communicate directly to a USB device. This
+back-end will of course not work with Bluetooth devices.
+
+### Test GUI
+
+HIDAPI also comes with a Test GUI. The Test GUI is cross-platform and uses
+Fox Toolkit <http://www.fox-toolkit.org>.  It will build on every platform
+which HIDAPI supports.  Since it relies on a 3rd party library, building it
+is optional but it is useful when debugging hardware.
+
+NOTE: Test GUI based on Fox Toolkit is not actively developed nor supported
+by HIDAPI team. It is kept as a historical artifact. It may even work sometime
+or on some platforms, but it is not going to get any new features or bugfixes.
+
+Instructions for installing Fox-Toolkit on each platform is not provided.
+Make sure to use Fox-Toolkit v1.6 if you choose to use it.
+
+### Console Test App
+
+If you want to play around with your HID device before starting
+any development with HIDAPI and using a GUI app is not an option for you, you may try [`hidapitester`](https://github.com/todbot/hidapitester).
+
+This app has a console interface for most of the features supported
+by HIDAPI library.
+
+## What Does the API Look Like?
+
+The API provides the most commonly used HID functions including sending
+and receiving of input, output, and feature reports. The sample program,
+which communicates with a heavily hacked up version of the Microchip USB
+Generic HID sample looks like this (with error checking removed for
+simplicity):
+
+**Warning: Only run the code you understand, and only when it conforms to the
+device spec. Writing data (`hid_write`) at random to your HID devices can break them.**
+
+```c
+#include <stdio.h> // printf
+#include <wchar.h> // wchar_t
+
+#include <hidapi.h>
+
+#define MAX_STR 255
+
+int main(int argc, char* argv[])
+{
+	int res;
+	unsigned char buf[65];
+	wchar_t wstr[MAX_STR];
+	hid_device *handle;
+	int i;
+
+	// Initialize the hidapi library
+	res = hid_init();
+
+	// Open the device using the VID, PID,
+	// and optionally the Serial number.
+	handle = hid_open(0x4d8, 0x3f, NULL);
+	if (!handle) {
+		printf("Unable to open device\n");
+		hid_exit();
+ 		return 1;
+	}
+
+	// Read the Manufacturer String
+	res = hid_get_manufacturer_string(handle, wstr, MAX_STR);
+	printf("Manufacturer String: %ls\n", wstr);
+
+	// Read the Product String
+	res = hid_get_product_string(handle, wstr, MAX_STR);
+	printf("Product String: %ls\n", wstr);
+
+	// Read the Serial Number String
+	res = hid_get_serial_number_string(handle, wstr, MAX_STR);
+	printf("Serial Number String: (%d) %ls\n", wstr[0], wstr);
+
+	// Read Indexed String 1
+	res = hid_get_indexed_string(handle, 1, wstr, MAX_STR);
+	printf("Indexed String 1: %ls\n", wstr);
+
+	// Toggle LED (cmd 0x80). The first byte is the report number (0x0).
+	buf[0] = 0x0;
+	buf[1] = 0x80;
+	res = hid_write(handle, buf, 65);
+
+	// Request state (cmd 0x81). The first byte is the report number (0x0).
+	buf[0] = 0x0;
+	buf[1] = 0x81;
+	res = hid_write(handle, buf, 65);
+
+	// Read requested state
+	res = hid_read(handle, buf, 65);
+
+	// Print out the returned buffer.
+	for (i = 0; i < 4; i++)
+		printf("buf[%d]: %d\n", i, buf[i]);
+
+	// Close the device
+	hid_close(handle);
+
+	// Finalize the hidapi library
+	res = hid_exit();
+
+	return 0;
+}
+```
+
+You can also use [hidtest/test.c](hidtest/test.c)
+as a starting point for your applications.
+
+
+## License
+
+HIDAPI may be used by one of three licenses as outlined in [LICENSE.txt](LICENSE.txt).
+
+## Installing HIDAPI
+
+If you want to build your own application that uses HID devices with HIDAPI,
+you need to get HIDAPI development package.
+
+Depending on what your development environment is, HIDAPI likely to be provided
+by your package manager.
+
+For instance on Ubuntu, HIDAPI is available via APT:
+```sh
+sudo apt install libhidapi-dev
+```
+
+HIDAPI package name for other systems/package managers may differ.
+Check the documentation/package list of your package manager.
+
+## Build from Source
+
+Check [BUILD.md](BUILD.md) for details.

+ 0 - 339
src/hidapi/README.txt

@@ -1,339 +0,0 @@
-         HIDAPI library for Windows, Linux, FreeBSD and Mac OS X
-        =========================================================
-
-About
-======
-
-HIDAPI is a multi-platform library which allows an application to interface
-with USB and Bluetooth HID-Class devices on Windows, Linux, FreeBSD, and Mac
-OS X.  HIDAPI can be either built as a shared library (.so or .dll) or
-can be embedded directly into a target application by adding a single source
-file (per platform) and a single header.
-
-HIDAPI has four back-ends:
-	* Windows (using hid.dll)
-	* Linux/hidraw (using the Kernel's hidraw driver)
-	* Linux/libusb (using libusb-1.0)
-	* FreeBSD (using libusb-1.0)
-	* Mac (using IOHidManager)
-
-On Linux, either the hidraw or the libusb back-end can be used. There are
-tradeoffs, and the functionality supported is slightly different.
-
-Linux/hidraw (linux/hid.c):
-This back-end uses the hidraw interface in the Linux kernel.  While this
-back-end will support both USB and Bluetooth, it has some limitations on
-kernels prior to 2.6.39, including the inability to send or receive feature
-reports.  In addition, it will only communicate with devices which have
-hidraw nodes associated with them.  Keyboards, mice, and some other devices
-which are blacklisted from having hidraw nodes will not work. Fortunately,
-for nearly all the uses of hidraw, this is not a problem.
-
-Linux/FreeBSD/libusb (libusb/hid.c):
-This back-end uses libusb-1.0 to communicate directly to a USB device. This
-back-end will of course not work with Bluetooth devices.
-
-HIDAPI also comes with a Test GUI. The Test GUI is cross-platform and uses
-Fox Toolkit (http://www.fox-toolkit.org).  It will build on every platform
-which HIDAPI supports.  Since it relies on a 3rd party library, building it
-is optional but recommended because it is so useful when debugging hardware.
-
-What Does the API Look Like?
-=============================
-The API provides the the most commonly used HID functions including sending
-and receiving of input, output, and feature reports.  The sample program,
-which communicates with a heavily hacked up version of the Microchip USB
-Generic HID sample looks like this (with error checking removed for
-simplicity):
-
-#ifdef WIN32
-#include <windows.h>
-#endif
-#include <stdio.h>
-#include <stdlib.h>
-#include "hidapi.h"
-
-#define MAX_STR 255
-
-int main(int argc, char* argv[])
-{
-	int res;
-	unsigned char buf[65];
-	wchar_t wstr[MAX_STR];
-	hid_device *handle;
-	int i;
-
-	// Initialize the hidapi library
-	res = hid_init();
-
-	// Open the device using the VID, PID,
-	// and optionally the Serial number.
-	handle = hid_open(0x4d8, 0x3f, NULL);
-
-	// Read the Manufacturer String
-	res = hid_get_manufacturer_string(handle, wstr, MAX_STR);
-	wprintf(L"Manufacturer String: %s\n", wstr);
-
-	// Read the Product String
-	res = hid_get_product_string(handle, wstr, MAX_STR);
-	wprintf(L"Product String: %s\n", wstr);
-
-	// Read the Serial Number String
-	res = hid_get_serial_number_string(handle, wstr, MAX_STR);
-	wprintf(L"Serial Number String: (%d) %s\n", wstr[0], wstr);
-
-	// Read Indexed String 1
-	res = hid_get_indexed_string(handle, 1, wstr, MAX_STR);
-	wprintf(L"Indexed String 1: %s\n", wstr);
-
-	// Toggle LED (cmd 0x80). The first byte is the report number (0x0).
-	buf[0] = 0x0;
-	buf[1] = 0x80;
-	res = hid_write(handle, buf, 65);
-
-	// Request state (cmd 0x81). The first byte is the report number (0x0).
-	buf[0] = 0x0;
-	buf[1] = 0x81;
-	res = hid_write(handle, buf, 65);
-
-	// Read requested state
-	res = hid_read(handle, buf, 65);
-
-	// Print out the returned buffer.
-	for (i = 0; i < 4; i++)
-		printf("buf[%d]: %d\n", i, buf[i]);
-
-	// Finalize the hidapi library
-	res = hid_exit();
-
-	return 0;
-}
-
-If you have your own simple test programs which communicate with standard
-hardware development boards (such as those from Microchip, TI, Atmel,
-FreeScale and others), please consider sending me something like the above
-for inclusion into the HIDAPI source.  This will help others who have the
-same hardware as you do.
-
-License
-========
-HIDAPI may be used by one of three licenses as outlined in LICENSE.txt.
-
-Download
-=========
-HIDAPI can be downloaded from github
-	git clone git://github.com/libusb/hidapi.git
-
-Build Instructions
-===================
-
-This section is long. Don't be put off by this. It's not long because it's
-complicated to build HIDAPI; it's quite the opposite.  This section is long
-because of the flexibility of HIDAPI and the large number of ways in which
-it can be built and used.  You will likely pick a single build method.
-
-HIDAPI can be built in several different ways. If you elect to build a
-shared library, you will need to build it from the HIDAPI source
-distribution.  If you choose instead to embed HIDAPI directly into your
-application, you can skip the building and look at the provided platform
-Makefiles for guidance.  These platform Makefiles are located in linux/
-libusb/ mac/ and windows/ and are called Makefile-manual.  In addition,
-Visual Studio projects are provided.  Even if you're going to embed HIDAPI
-into your project, it is still beneficial to build the example programs.
-
-
-Prerequisites:
----------------
-
-	Linux:
-	-------
-	On Linux, you will need to install development packages for libudev,
-	libusb and optionally Fox-toolkit (for the test GUI). On
-	Debian/Ubuntu systems these can be installed by running:
-	    sudo apt-get install libudev-dev libusb-1.0-0-dev libfox-1.6-dev
-
-	If you downloaded the source directly from the git repository (using
-	git clone), you'll need Autotools:
-	    sudo apt-get install autotools-dev autoconf automake libtool
-
-	FreeBSD:
-	---------
-	On FreeBSD you will need to install GNU make, libiconv, and
-	optionally Fox-Toolkit (for the test GUI). This is done by running
-	the following:
-	    pkg_add -r gmake libiconv fox16
-
-	If you downloaded the source directly from the git repository (using
-	git clone), you'll need Autotools:
-	    pkg_add -r autotools
-
-	Mac:
-	-----
-	On Mac, you will need to install Fox-Toolkit if you wish to build
-	the Test GUI. There are two ways to do this, and each has a slight
-	complication. Which method you use depends on your use case.
-
-	If you wish to build the Test GUI just for your own testing on your
-	own computer, then the easiest method is to install Fox-Toolkit
-	using ports:
-		sudo port install fox
-
-	If you wish to build the TestGUI app bundle to redistribute to
-	others, you will need to install Fox-toolkit from source.  This is
-	because the version of fox that gets installed using ports uses the
-	ports X11 libraries which are not compatible with the Apple X11
-	libraries.  If you install Fox with ports and then try to distribute
-	your built app bundle, it will simply fail to run on other systems.
-	To install Fox-Toolkit manually, download the source package from
-	http://www.fox-toolkit.org, extract it, and run the following from
-	within the extracted source:
-		./configure && make && make install
-
-	Windows:
-	---------
-	On Windows, if you want to build the test GUI, you will need to get
-	the hidapi-externals.zip package from the download site.  This
-	contains pre-built binaries for Fox-toolkit.  Extract
-	hidapi-externals.zip just outside of hidapi, so that
-	hidapi-externals and hidapi are on the same level, as shown:
-
-	     Parent_Folder
-	       |
-	       +hidapi
-	       +hidapi-externals
-
-	Again, this step is not required if you do not wish to build the
-	test GUI.
-
-
-Building HIDAPI into a shared library on Unix Platforms:
----------------------------------------------------------
-
-On Unix-like systems such as Linux, FreeBSD, Mac, and even Windows, using
-Mingw or Cygwin, the easiest way to build a standard system-installed shared
-library is to use the GNU Autotools build system.  If you checked out the
-source from the git repository, run the following:
-
-	./bootstrap
-	./configure
-	make
-	make install     <----- as root, or using sudo
-
-If you downloaded a source package (ie: if you did not run git clone), you
-can skip the ./bootstrap step.
-
-./configure can take several arguments which control the build. The two most
-likely to be used are:
-	--enable-testgui
-		Enable build of the Test GUI. This requires Fox toolkit to
-		be installed.  Instructions for installing Fox-Toolkit on
-		each platform are in the Prerequisites section above.
-
-	--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.
-
-Building the manual way on Unix platforms:
--------------------------------------------
-
-Manual Makefiles are provided mostly to give the user and 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 the Autotools method described above.
-
-	To build HIDAPI using the manual makefiles, change to the directory
-	of your platform and run make. For example, on Linux run:
-		cd linux/
-		make -f Makefile-manual
-
-	To build the Test GUI using the manual makefiles:
-		cd testgui/
-		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 the Test GUI on windows using Visual Studio, build the .sln file in
-the testgui/ directory.
-
-To build HIDAPI using MinGW or Cygwin using Autotools, use the instructions
-in the section titled "Building HIDAPI into a shared library on Unix
-Platforms" above.  Note that building the Test GUI with MinGW or Cygwin will
-require the Windows procedure in the Prerequisites section above (ie:
-hidapi-externals.zip).
-
-To build HIDAPI using MinGW using the Manual Makefiles, see the section
-"Building the manual way on Unix platforms" above.
-
-HIDAPI can also be built using the Windows DDK (now also called the Windows
-Driver Kit or WDK). This method was originally required for the HIDAPI build
-but not anymore. However, some users still prefer this method. It is not as
-well supported anymore but should still work. Patches are welcome if it does
-not. To build using the DDK:
-
-   1. Install the Windows Driver Kit (WDK) from Microsoft.
-   2. From the Start menu, in the Windows Driver Kits folder, select Build
-      Environments, then your operating system, then the x86 Free Build
-      Environment (or one that is appropriate for your system).
-   3. From the console, change directory to the windows/ddk_build/ directory,
-      which is part of the HIDAPI distribution.
-   4. Type build.
-   5. You can find the output files (DLL and LIB) in a subdirectory created
-      by the build system which is appropriate for your environment. On
-      Windows XP, this directory is objfre_wxp_x86/i386.
-
-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.
-
-	$ export STAGING=$HOME/out
-	$ export HOST=arm-linux
-
-STAGING and HOST can be modified to suit your setup.
-
-Prerequisites
---------------
-
-Note that the build of libudev is the very basic configuration.
-
-Build Libusb. From the libusb source directory, run:
-	./configure --host=$HOST --prefix=$STAGING
-	make
-	make install
-
-Build libudev. From the libudev source directory, run:
-	./configure --disable-gudev --disable-introspection --disable-hwdb \
-		 --host=$HOST --prefix=$STAGING
-	make
-	make install
-
-Building HIDAPI
-----------------
-
-Build HIDAPI:
-
-	PKG_CONFIG_DIR= \
-	PKG_CONFIG_LIBDIR=$STAGING/lib/pkgconfig:$STAGING/share/pkgconfig \
-	PKG_CONFIG_SYSROOT_DIR=$STAGING \
-	./configure --host=$HOST --prefix=$STAGING
-
-
-Signal 11 Software - 2010-04-11
-                     2010-07-28
-                     2011-09-10
-                     2012-05-01
-                     2012-07-03

+ 1 - 0
src/hidapi/VERSION

@@ -0,0 +1 @@
+0.14.0

+ 0 - 1443
src/hidapi/android/hid.cpp

@@ -1,1443 +0,0 @@
-/*
-  Simple DirectMedia Layer
-  Copyright (C) 2022 Valve Corporation
-
-  This software is provided 'as-is', without any express or implied
-  warranty.  In no event will the authors be held liable for any damages
-  arising from the use of this software.
-
-  Permission is granted to anyone to use this software for any purpose,
-  including commercial applications, and to alter it and redistribute it
-  freely, subject to the following restrictions:
-
-  1. The origin of this software must not be misrepresented; you must not
-     claim that you wrote the original software. If you use this software
-     in a product, an acknowledgment in the product documentation would be
-     appreciated but is not required.
-  2. Altered source versions must be plainly marked as such, and must not be
-     misrepresented as being the original software.
-  3. This notice may not be removed or altered from any source distribution.
-*/
-#include "SDL_internal.h"
-
-// Purpose: A wrapper implementing "HID" API for Android
-//
-//          This layer glues the hidapi API to Android's USB and BLE stack.
-
-#include "hid.h"
-
-// Common to stub version and non-stub version of functions
-#include <jni.h>
-#include <android/log.h>
-
-#define TAG "hidapi"
-
-// Have error log always available
-#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, TAG, __VA_ARGS__)
-
-#ifdef DEBUG
-#define LOGV(...) __android_log_print(ANDROID_LOG_VERBOSE, TAG, __VA_ARGS__)
-#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, TAG, __VA_ARGS__)
-#else
-#define LOGV(...)
-#define LOGD(...)
-#endif
-
-#define SDL_JAVA_PREFIX                                 org_libsdl_app
-#define CONCAT1(prefix, class, function)                CONCAT2(prefix, class, function)
-#define CONCAT2(prefix, class, function)                Java_ ## prefix ## _ ## class ## _ ## function
-#define HID_DEVICE_MANAGER_JAVA_INTERFACE(function)     CONCAT1(SDL_JAVA_PREFIX, HIDDeviceManager, function)
-
-
-#ifndef SDL_HIDAPI_DISABLED
-
-#include "../../core/android/SDL_android.h"
-
-#define hid_init                        PLATFORM_hid_init
-#define hid_exit                        PLATFORM_hid_exit
-#define hid_enumerate                   PLATFORM_hid_enumerate
-#define hid_free_enumeration            PLATFORM_hid_free_enumeration
-#define hid_open                        PLATFORM_hid_open
-#define hid_open_path                   PLATFORM_hid_open_path
-#define hid_write                       PLATFORM_hid_write
-#define hid_read_timeout                PLATFORM_hid_read_timeout
-#define hid_read                        PLATFORM_hid_read
-#define hid_set_nonblocking             PLATFORM_hid_set_nonblocking
-#define hid_send_feature_report         PLATFORM_hid_send_feature_report
-#define hid_get_feature_report          PLATFORM_hid_get_feature_report
-#define hid_close                       PLATFORM_hid_close
-#define hid_get_manufacturer_string     PLATFORM_hid_get_manufacturer_string
-#define hid_get_product_string          PLATFORM_hid_get_product_string
-#define hid_get_serial_number_string    PLATFORM_hid_get_serial_number_string
-#define hid_get_indexed_string          PLATFORM_hid_get_indexed_string
-#define hid_error                       PLATFORM_hid_error
-
-#include <pthread.h>
-#include <errno.h>	// For ETIMEDOUT and ECONNRESET
-#include <stdlib.h> // For malloc() and free()
-
-#include "../hidapi/hidapi.h"
-
-typedef uint32_t uint32;
-typedef uint64_t uint64;
-
-
-struct hid_device_
-{
-	int m_nId;
-	int m_nDeviceRefCount;
-};
-
-static JavaVM *g_JVM;
-static pthread_key_t g_ThreadKey;
-
-template<class T>
-class hid_device_ref
-{
-public:
-	hid_device_ref( T *pObject = nullptr ) : m_pObject( nullptr )
-	{
-		SetObject( pObject );
-	}
-
-	hid_device_ref( const hid_device_ref &rhs ) : m_pObject( nullptr )
-	{
-		SetObject( rhs.GetObject() );
-	}
-
-	~hid_device_ref()
-	{
-		SetObject( nullptr );
-	}
-
-	void SetObject( T *pObject )
-	{
-		if ( m_pObject && m_pObject->DecrementRefCount() == 0 )
-		{
-			delete m_pObject;
-		}
-
-		m_pObject = pObject;
-
-		if ( m_pObject )
-		{
-			m_pObject->IncrementRefCount();
-		}
-	}
-
-	hid_device_ref &operator =( T *pObject )
-	{
-		SetObject( pObject );
-		return *this;
-	}
-
-	hid_device_ref &operator =( const hid_device_ref &rhs )
-	{
-		SetObject( rhs.GetObject() );
-		return *this;
-	}
-
-	T *GetObject() const
-	{
-		return m_pObject;
-	}
-
-	T* operator->() const
-	{
-		return m_pObject;
-	}
-
-	operator bool() const
-	{
-		return ( m_pObject != nullptr );
-	}
-
-private:
-	T *m_pObject;
-};
-
-class hid_mutex_guard
-{
-public:
-	hid_mutex_guard( pthread_mutex_t *pMutex ) : m_pMutex( pMutex )
-	{
-		pthread_mutex_lock( m_pMutex );
-	}
-	~hid_mutex_guard()
-	{
-		pthread_mutex_unlock( m_pMutex );
-	}
-
-private:
-	pthread_mutex_t *m_pMutex;
-};
-
-class hid_buffer
-{
-public:
-	hid_buffer() : m_pData( nullptr ), m_nSize( 0 ), m_nAllocated( 0 )
-	{
-	}
-
-	hid_buffer( const uint8_t *pData, size_t nSize ) : m_pData( nullptr ), m_nSize( 0 ), m_nAllocated( 0 )
-	{
-		assign( pData, nSize );
-	}
-
-	~hid_buffer()
-	{
-		delete[] m_pData;
-	}
-
-	void assign( const uint8_t *pData, size_t nSize )
-	{
-		if ( nSize > m_nAllocated )
-		{
-			delete[] m_pData;
-			m_pData = new uint8_t[ nSize ];
-			m_nAllocated = nSize;
-		}
-
-		m_nSize = nSize;
-		SDL_memcpy( m_pData, pData, nSize );
-	}
-
-	void clear()
-	{
-		m_nSize = 0;
-	}
-
-	size_t size() const
-	{
-		return m_nSize;
-	}
-
-	const uint8_t *data() const
-	{
-		return m_pData;
-	}
-
-private:
-	uint8_t *m_pData;
-	size_t m_nSize;
-	size_t m_nAllocated;
-};
-
-class hid_buffer_pool
-{
-public:
-	hid_buffer_pool() : m_nSize( 0 ), m_pHead( nullptr ), m_pTail( nullptr ), m_pFree( nullptr )
-	{
-	}
-
-	~hid_buffer_pool()
-	{
-		clear();
-
-		while ( m_pFree )
-		{
-			hid_buffer_entry *pEntry = m_pFree;
-			m_pFree = m_pFree->m_pNext;
-			delete pEntry;
-		}
-	}
-
-	size_t size() const { return m_nSize; }
-
-	const hid_buffer &front() const { return m_pHead->m_buffer; }
-
-	void pop_front()
-	{
-		hid_buffer_entry *pEntry = m_pHead;
-		if ( pEntry )
-		{
-			m_pHead = pEntry->m_pNext;
-			if ( !m_pHead )
-			{
-				m_pTail = nullptr;
-			}
-			pEntry->m_pNext = m_pFree;
-			m_pFree = pEntry;
-			--m_nSize;
-		}
-	}
-
-	void emplace_back( const uint8_t *pData, size_t nSize )
-	{
-		hid_buffer_entry *pEntry;
-
-		if ( m_pFree )
-		{
-			pEntry = m_pFree;
-			m_pFree = m_pFree->m_pNext;
-		}
-		else
-		{
-			pEntry = new hid_buffer_entry;
-		}
-		pEntry->m_pNext = nullptr;
-
-		if ( m_pTail )
-		{
-			m_pTail->m_pNext = pEntry;
-		}
-		else
-		{
-			m_pHead = pEntry;
-		}
-		m_pTail = pEntry;
-
-		pEntry->m_buffer.assign( pData, nSize );
-		++m_nSize;
-	}
-
-	void clear()
-	{
-		while ( size() > 0 )
-		{
-			pop_front();
-		}
-	}
-
-private:
-	struct hid_buffer_entry
-	{
-		hid_buffer m_buffer;
-		hid_buffer_entry *m_pNext;
-	};
-
-	size_t m_nSize;
-	hid_buffer_entry *m_pHead;
-	hid_buffer_entry *m_pTail;
-	hid_buffer_entry *m_pFree;
-};
-
-static jbyteArray NewByteArray( JNIEnv* env, const uint8_t *pData, size_t nDataLen )
-{
-	jbyteArray array = env->NewByteArray( (jsize)nDataLen );
-	jbyte *pBuf = env->GetByteArrayElements( array, NULL );
-	SDL_memcpy( pBuf, pData, nDataLen );
-	env->ReleaseByteArrayElements( array, pBuf, 0 );
-
-	return array;
-}
-
-static char *CreateStringFromJString( JNIEnv *env, const jstring &sString )
-{
-	size_t nLength = env->GetStringUTFLength( sString );
-	const char *pjChars = env->GetStringUTFChars( sString, NULL );
-	char *psString = (char*)malloc( nLength + 1 );
-	SDL_memcpy( psString, pjChars, nLength );
-	psString[ nLength ] = '\0';
-	env->ReleaseStringUTFChars( sString, pjChars );
-	return psString;
-}
-
-static wchar_t *CreateWStringFromJString( JNIEnv *env, const jstring &sString )
-{
-	size_t nLength = env->GetStringLength( sString );
-	const jchar *pjChars = env->GetStringChars( sString, NULL );
-	wchar_t *pwString = (wchar_t*)malloc( ( nLength + 1 ) * sizeof( wchar_t ) );
-	wchar_t *pwChars = pwString;
-	for ( size_t iIndex = 0; iIndex < nLength; ++iIndex )
-	{
-		pwChars[ iIndex ] = pjChars[ iIndex ];
-	}
-	pwString[ nLength ] = '\0';
-	env->ReleaseStringChars( sString, pjChars );
-	return pwString;
-}
-
-static wchar_t *CreateWStringFromWString( const wchar_t *pwSrc )
-{
-	size_t nLength = SDL_wcslen( pwSrc );
-	wchar_t *pwString = (wchar_t*)malloc( ( nLength + 1 ) * sizeof( wchar_t ) );
-	SDL_memcpy( pwString, pwSrc, nLength * sizeof( wchar_t ) );
-	pwString[ nLength ] = '\0';
-	return pwString;
-}
-
-static hid_device_info *CopyHIDDeviceInfo( const hid_device_info *pInfo )
-{
-	hid_device_info *pCopy = new hid_device_info;
-	*pCopy = *pInfo;
-	pCopy->path = SDL_strdup( pInfo->path );
-	pCopy->product_string = CreateWStringFromWString( pInfo->product_string );
-	pCopy->manufacturer_string = CreateWStringFromWString( pInfo->manufacturer_string );
-	pCopy->serial_number = CreateWStringFromWString( pInfo->serial_number );
-	return pCopy;
-}
-
-static void FreeHIDDeviceInfo( hid_device_info *pInfo )
-{
-	free( pInfo->path );
-	free( pInfo->serial_number );
-	free( pInfo->manufacturer_string );
-	free( pInfo->product_string );
-	delete pInfo;
-}
-
-static jclass  g_HIDDeviceManagerCallbackClass;
-static jobject g_HIDDeviceManagerCallbackHandler;
-static jmethodID g_midHIDDeviceManagerInitialize;
-static jmethodID g_midHIDDeviceManagerOpen;
-static jmethodID g_midHIDDeviceManagerSendOutputReport;
-static jmethodID g_midHIDDeviceManagerSendFeatureReport;
-static jmethodID g_midHIDDeviceManagerGetFeatureReport;
-static jmethodID g_midHIDDeviceManagerClose;
-static bool g_initialized = false;
-
-static uint64_t get_timespec_ms( const struct timespec &ts )
-{
-	return (uint64_t)ts.tv_sec * 1000 + ts.tv_nsec / 1000000;
-}
-
-static void ExceptionCheck( JNIEnv *env, const char *pszClassName, const char *pszMethodName )
-{
-	if ( env->ExceptionCheck() )
-	{
-		// Get our exception
-		jthrowable jExcept = env->ExceptionOccurred();
-
-		// Clear the exception so we can call JNI again
-		env->ExceptionClear();
-
-		// Get our exception message
-		jclass jExceptClass = env->GetObjectClass( jExcept );
-		jmethodID jMessageMethod = env->GetMethodID( jExceptClass, "getMessage", "()Ljava/lang/String;" );
-		jstring jMessage = (jstring)( env->CallObjectMethod( jExcept, jMessageMethod ) );
-		const char *pszMessage = env->GetStringUTFChars( jMessage, NULL );
-
-		// ...and log it.
-		LOGE( "%s%s%s threw an exception: %s",
-			pszClassName ? pszClassName : "",
-			pszClassName ? "::" : "",
-			pszMethodName, pszMessage );
-
-		// Cleanup
-		env->ReleaseStringUTFChars( jMessage, pszMessage );
-		env->DeleteLocalRef( jMessage );
-		env->DeleteLocalRef( jExceptClass );
-		env->DeleteLocalRef( jExcept );
-	}
-}
-
-class CHIDDevice
-{
-public:
-	CHIDDevice( int nDeviceID, hid_device_info *pInfo )
-	{
-		m_nId = nDeviceID;
-		m_pInfo = pInfo;
-
-		// The Bluetooth Steam Controller needs special handling
-		const int VALVE_USB_VID	= 0x28DE;
-		const int D0G_BLE2_PID = 0x1106;
-		if ( pInfo->vendor_id == VALVE_USB_VID && pInfo->product_id == D0G_BLE2_PID )
-		{
-			m_bIsBLESteamController = true;
-		}
-	}
-
-	~CHIDDevice()
-	{
-		FreeHIDDeviceInfo( m_pInfo );
-
-		// Note that we don't delete m_pDevice, as the app may still have a reference to it
-	}
-
-	int IncrementRefCount()
-	{
-		int nValue;
-		pthread_mutex_lock( &m_refCountLock );
-		nValue = ++m_nRefCount;
-		pthread_mutex_unlock( &m_refCountLock );
-		return nValue;
-	}
-
-	int DecrementRefCount()
-	{
-		int nValue;
-		pthread_mutex_lock( &m_refCountLock );
-		nValue = --m_nRefCount;
-		pthread_mutex_unlock( &m_refCountLock );
-		return nValue;
-	}
-
-	int GetId()
-	{
-		return m_nId;
-	}
-
-	const hid_device_info *GetDeviceInfo()
-	{
-		return m_pInfo;
-	}
-
-	hid_device *GetDevice()
-	{
-		return m_pDevice;
-	}
-
-	void ExceptionCheck( JNIEnv *env, const char *pszMethodName )
-	{
-		::ExceptionCheck( env, "CHIDDevice", pszMethodName );
-	}
-
-	bool BOpen()
-	{
-		// Make sure thread is attached to JVM/env
-		JNIEnv *env;
-		g_JVM->AttachCurrentThread( &env, NULL );
-		pthread_setspecific( g_ThreadKey, (void*)env );
-
-		if ( !g_HIDDeviceManagerCallbackHandler )
-		{
-			LOGV( "Device open without callback handler" );
-			return false;
-		}
-
-		m_bIsWaitingForOpen = false;
-		m_bOpenResult = env->CallBooleanMethod( g_HIDDeviceManagerCallbackHandler, g_midHIDDeviceManagerOpen, m_nId );
-		ExceptionCheck( env, "BOpen" );
-
-		if ( m_bIsWaitingForOpen )
-		{
-			hid_mutex_guard cvl( &m_cvLock );
-
-			const int OPEN_TIMEOUT_SECONDS = 60;
-			struct timespec ts, endtime;
-			clock_gettime( CLOCK_REALTIME, &ts );
-			endtime = ts;
-			endtime.tv_sec += OPEN_TIMEOUT_SECONDS;
-			do
-			{
-				if ( pthread_cond_timedwait( &m_cv, &m_cvLock, &endtime ) != 0 )
-				{
-					break;
-				}
-			}
-			while ( m_bIsWaitingForOpen && get_timespec_ms( ts ) < get_timespec_ms( endtime ) );
-		}
-
-		if ( !m_bOpenResult )
-		{
-			if ( m_bIsWaitingForOpen )
-			{
-				LOGV( "Device open failed - timed out waiting for device permission" );
-			}
-			else
-			{
-				LOGV( "Device open failed" );
-			}
-			return false;
-		}
-
-		m_pDevice = new hid_device;
-		m_pDevice->m_nId = m_nId;
-		m_pDevice->m_nDeviceRefCount = 1;
-		LOGD("Creating device %d (%p), refCount = 1\n", m_pDevice->m_nId, m_pDevice);
-		return true;
-	}
-
-	void SetOpenPending()
-	{
-		m_bIsWaitingForOpen = true;
-	}
-
-	void SetOpenResult( bool bResult )
-	{
-		if ( m_bIsWaitingForOpen )
-		{
-			m_bOpenResult = bResult;
-			m_bIsWaitingForOpen = false;
-			pthread_cond_signal( &m_cv );
-		}
-	}
-
-	void ProcessInput( const uint8_t *pBuf, size_t nBufSize )
-	{
-		hid_mutex_guard l( &m_dataLock );
-
-		size_t MAX_REPORT_QUEUE_SIZE = 16;
-		if ( m_vecData.size() >= MAX_REPORT_QUEUE_SIZE )
-		{
-			m_vecData.pop_front();
-		}
-		m_vecData.emplace_back( pBuf, nBufSize );
-	}
-
-	int GetInput( unsigned char *data, size_t length )
-	{
-		hid_mutex_guard l( &m_dataLock );
-
-		if ( m_vecData.size() == 0 )
-		{
-//			LOGV( "hid_read_timeout no data available" );
-			return 0;
-		}
-
-		const hid_buffer &buffer = m_vecData.front();
-		size_t nDataLen = buffer.size() > length ? length : buffer.size();
-		if ( m_bIsBLESteamController )
-		{
-			data[0] = 0x03;
-			SDL_memcpy( data + 1, buffer.data(), nDataLen );
-			++nDataLen;
-		}
-		else
-		{
-			SDL_memcpy( data, buffer.data(), nDataLen );
-		}
-		m_vecData.pop_front();
-
-//		LOGV("Read %u bytes", nDataLen);
-//		LOGV("%02x %02x %02x %02x %02x %02x %02x %02x ....",
-//			 data[0], data[1], data[2], data[3],
-//			 data[4], data[5], data[6], data[7]);
-
-		return (int)nDataLen;
-	}
-
-	int SendOutputReport( const unsigned char *pData, size_t nDataLen )
-	{
-		// Make sure thread is attached to JVM/env
-		JNIEnv *env;
-		g_JVM->AttachCurrentThread( &env, NULL );
-		pthread_setspecific( g_ThreadKey, (void*)env );
-
-		int nRet = -1;
-		if ( g_HIDDeviceManagerCallbackHandler )
-		{
-			jbyteArray pBuf = NewByteArray( env, pData, nDataLen );
-			nRet = env->CallIntMethod( g_HIDDeviceManagerCallbackHandler, g_midHIDDeviceManagerSendOutputReport, m_nId, pBuf );
-			ExceptionCheck( env, "SendOutputReport" );
-			env->DeleteLocalRef( pBuf );
-		}
-		else
-		{
-			LOGV( "SendOutputReport without callback handler" );
-		}
-		return nRet;
-	}
-
-	int SendFeatureReport( const unsigned char *pData, size_t nDataLen )
-	{
-		// Make sure thread is attached to JVM/env
-		JNIEnv *env;
-		g_JVM->AttachCurrentThread( &env, NULL );
-		pthread_setspecific( g_ThreadKey, (void*)env );
-
-		int nRet = -1;
-		if ( g_HIDDeviceManagerCallbackHandler )
-		{
-			jbyteArray pBuf = NewByteArray( env, pData, nDataLen );
-			nRet = env->CallIntMethod( g_HIDDeviceManagerCallbackHandler, g_midHIDDeviceManagerSendFeatureReport, m_nId, pBuf );
-			ExceptionCheck( env, "SendFeatureReport" );
-			env->DeleteLocalRef( pBuf );
-		}
-		else
-		{
-			LOGV( "SendFeatureReport without callback handler" );
-		}
-		return nRet;
-	}
-
-	void ProcessFeatureReport( const uint8_t *pBuf, size_t nBufSize )
-	{
-		hid_mutex_guard cvl( &m_cvLock );
-		if ( m_bIsWaitingForFeatureReport )
-		{
-			m_featureReport.assign( pBuf, nBufSize );
-
-			m_bIsWaitingForFeatureReport = false;
-			m_nFeatureReportError = 0;
-			pthread_cond_signal( &m_cv );
-		}
-	}
-
-	int GetFeatureReport( unsigned char *pData, size_t nDataLen )
-	{
-		// Make sure thread is attached to JVM/env
-		JNIEnv *env;
-		g_JVM->AttachCurrentThread( &env, NULL );
-		pthread_setspecific( g_ThreadKey, (void*)env );
-
-		if ( !g_HIDDeviceManagerCallbackHandler )
-		{
-			LOGV( "GetFeatureReport without callback handler" );
-			return -1;
-		}
-
-		{
-			hid_mutex_guard cvl( &m_cvLock );
-			if ( m_bIsWaitingForFeatureReport )
-			{
-				LOGV( "Get feature report already ongoing... bail" );
-				return -1; // Read already ongoing, we currently do not serialize, TODO
-			}
-			m_bIsWaitingForFeatureReport = true;
-		}
-
-		jbyteArray pBuf = NewByteArray( env, pData, nDataLen );
-		int nRet = env->CallBooleanMethod( g_HIDDeviceManagerCallbackHandler, g_midHIDDeviceManagerGetFeatureReport, m_nId, pBuf ) ? 0 : -1;
-		ExceptionCheck( env, "GetFeatureReport" );
-		env->DeleteLocalRef( pBuf );
-		if ( nRet < 0 )
-		{
-			LOGV( "GetFeatureReport failed" );
-			m_bIsWaitingForFeatureReport = false;
-			return -1;
-		}
-
-		{
-			hid_mutex_guard cvl( &m_cvLock );
-			if ( m_bIsWaitingForFeatureReport )
-			{
-				LOGV("=== Going to sleep" );
-				// Wait in CV until we are no longer waiting for a feature report.
-				const int FEATURE_REPORT_TIMEOUT_SECONDS = 2;
-				struct timespec ts, endtime;
-				clock_gettime( CLOCK_REALTIME, &ts );
-				endtime = ts;
-				endtime.tv_sec += FEATURE_REPORT_TIMEOUT_SECONDS;
-				do
-				{
-					if ( pthread_cond_timedwait( &m_cv, &m_cvLock, &endtime ) != 0 )
-					{
-						break;
-					}
-				}
-				while ( m_bIsWaitingForFeatureReport && get_timespec_ms( ts ) < get_timespec_ms( endtime ) );
-
-				// We are back
-				if ( m_bIsWaitingForFeatureReport )
-				{
-					m_nFeatureReportError = -ETIMEDOUT;
-					m_bIsWaitingForFeatureReport = false;
-				}
-				LOGV( "=== Got feature report err=%d", m_nFeatureReportError );
-				if ( m_nFeatureReportError != 0 )
-				{
-					return m_nFeatureReportError;
-				}
-			}
-
-			size_t uBytesToCopy = m_featureReport.size() > nDataLen ? nDataLen : m_featureReport.size();
-			SDL_memcpy( pData, m_featureReport.data(), uBytesToCopy );
-			m_featureReport.clear();
-			LOGV( "=== Got %u bytes", uBytesToCopy );
-
-			return (int)uBytesToCopy;
-		}
-	}
-
-	void Close( bool bDeleteDevice )
-	{
-		// Make sure thread is attached to JVM/env
-		JNIEnv *env;
-		g_JVM->AttachCurrentThread( &env, NULL );
-		pthread_setspecific( g_ThreadKey, (void*)env );
-
-		if ( g_HIDDeviceManagerCallbackHandler )
-		{
-			env->CallVoidMethod( g_HIDDeviceManagerCallbackHandler, g_midHIDDeviceManagerClose, m_nId );
-			ExceptionCheck( env, "Close" );
-		}
-
-		hid_mutex_guard dataLock( &m_dataLock );
-		m_vecData.clear();
-
-		// Clean and release pending feature report reads
-		hid_mutex_guard cvLock( &m_cvLock );
-		m_featureReport.clear();
-		m_bIsWaitingForFeatureReport = false;
-		m_nFeatureReportError = -ECONNRESET;
-		pthread_cond_broadcast( &m_cv );
-
-		if ( bDeleteDevice )
-		{
-			delete m_pDevice;
-			m_pDevice = nullptr;
-		}
-	}
-
-private:
-	pthread_mutex_t m_refCountLock = PTHREAD_MUTEX_INITIALIZER;
-	int m_nRefCount = 0;
-	int m_nId = 0;
-	hid_device_info *m_pInfo = nullptr;
-	hid_device *m_pDevice = nullptr;
-	bool m_bIsBLESteamController = false;
-
-	pthread_mutex_t m_dataLock = PTHREAD_MUTEX_INITIALIZER; // This lock has to be held to access m_vecData
-	hid_buffer_pool m_vecData;
-
-	// For handling get_feature_report
-	pthread_mutex_t m_cvLock = PTHREAD_MUTEX_INITIALIZER; // This lock has to be held to access any variables below
-	pthread_cond_t m_cv = PTHREAD_COND_INITIALIZER;
-	bool m_bIsWaitingForOpen = false;
-	bool m_bOpenResult = false;
-	bool m_bIsWaitingForFeatureReport = false;
-	int m_nFeatureReportError = 0;
-	hid_buffer m_featureReport;
-
-public:
-	hid_device_ref<CHIDDevice> next;
-};
-
-class CHIDDevice;
-static pthread_mutex_t g_DevicesMutex = PTHREAD_MUTEX_INITIALIZER;
-static pthread_mutex_t g_DevicesRefCountMutex = PTHREAD_MUTEX_INITIALIZER;
-static hid_device_ref<CHIDDevice> g_Devices;
-
-static hid_device_ref<CHIDDevice> FindDevice( int nDeviceId )
-{
-	hid_device_ref<CHIDDevice> pDevice;
-
-	hid_mutex_guard l( &g_DevicesMutex );
-	for ( pDevice = g_Devices; pDevice; pDevice = pDevice->next )
-	{
-		if ( pDevice->GetId() == nDeviceId )
-		{
-			break;
-		}
-	}
-	return pDevice;
-}
-
-static void ThreadDestroyed(void* value)
-{
-	/* The thread is being destroyed, detach it from the Java VM and set the g_ThreadKey value to NULL as required */
-	JNIEnv *env = (JNIEnv*) value;
-	if (env != NULL) {
-		g_JVM->DetachCurrentThread();
-		pthread_setspecific(g_ThreadKey, NULL);
-	}
-}
-
-
-extern "C"
-JNIEXPORT void JNICALL HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceRegisterCallback)(JNIEnv *env, jobject thiz);
-
-extern "C"
-JNIEXPORT void JNICALL HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceReleaseCallback)(JNIEnv *env, jobject thiz);
-
-extern "C"
-JNIEXPORT void JNICALL HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceConnected)(JNIEnv *env, jobject thiz, int nDeviceID, jstring sIdentifier, int nVendorId, int nProductId, jstring sSerialNumber, int nReleaseNumber, jstring sManufacturer, jstring sProduct, int nInterface, int nInterfaceClass, int nInterfaceSubclass, int nInterfaceProtocol );
-
-extern "C"
-JNIEXPORT void JNICALL HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceOpenPending)(JNIEnv *env, jobject thiz, int nDeviceID);
-
-extern "C"
-JNIEXPORT void JNICALL HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceOpenResult)(JNIEnv *env, jobject thiz, int nDeviceID, bool bOpened);
-
-extern "C"
-JNIEXPORT void JNICALL HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceDisconnected)(JNIEnv *env, jobject thiz, int nDeviceID);
-
-extern "C"
-JNIEXPORT void JNICALL HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceInputReport)(JNIEnv *env, jobject thiz, int nDeviceID, jbyteArray value);
-
-extern "C"
-JNIEXPORT void JNICALL HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceFeatureReport)(JNIEnv *env, jobject thiz, int nDeviceID, jbyteArray value);
-
-
-extern "C"
-JNIEXPORT void JNICALL HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceRegisterCallback)(JNIEnv *env, jobject thiz )
-{
-	LOGV( "HIDDeviceRegisterCallback()");
-
-	env->GetJavaVM( &g_JVM );
-
-	/*
-	 * Create mThreadKey so we can keep track of the JNIEnv assigned to each thread
-	 * Refer to http://developer.android.com/guide/practices/design/jni.html for the rationale behind this
-	 */
-	if (pthread_key_create(&g_ThreadKey, ThreadDestroyed) != 0) {
-		__android_log_print(ANDROID_LOG_ERROR, TAG, "Error initializing pthread key");
-	}
-
-	if ( g_HIDDeviceManagerCallbackHandler != NULL )
-	{
-		env->DeleteGlobalRef( g_HIDDeviceManagerCallbackClass );
-		g_HIDDeviceManagerCallbackClass = NULL;
-		env->DeleteGlobalRef( g_HIDDeviceManagerCallbackHandler );
-		g_HIDDeviceManagerCallbackHandler = NULL;
-	}
-
-	g_HIDDeviceManagerCallbackHandler = env->NewGlobalRef( thiz );
-	jclass objClass = env->GetObjectClass( thiz );
-	if ( objClass )
-	{
-		g_HIDDeviceManagerCallbackClass = reinterpret_cast< jclass >( env->NewGlobalRef( objClass ) );
-		g_midHIDDeviceManagerInitialize = env->GetMethodID( g_HIDDeviceManagerCallbackClass, "initialize", "(ZZ)Z" );
-		if ( !g_midHIDDeviceManagerInitialize )
-		{
-			__android_log_print(ANDROID_LOG_ERROR, TAG, "HIDDeviceRegisterCallback: callback class missing initialize" );
-		}
-		g_midHIDDeviceManagerOpen = env->GetMethodID( g_HIDDeviceManagerCallbackClass, "openDevice", "(I)Z" );
-		if ( !g_midHIDDeviceManagerOpen )
-		{
-			__android_log_print(ANDROID_LOG_ERROR, TAG, "HIDDeviceRegisterCallback: callback class missing openDevice" );
-		}
-		g_midHIDDeviceManagerSendOutputReport = env->GetMethodID( g_HIDDeviceManagerCallbackClass, "sendOutputReport", "(I[B)I" );
-		if ( !g_midHIDDeviceManagerSendOutputReport )
-		{
-			__android_log_print(ANDROID_LOG_ERROR, TAG, "HIDDeviceRegisterCallback: callback class missing sendOutputReport" );
-		}
-		g_midHIDDeviceManagerSendFeatureReport = env->GetMethodID( g_HIDDeviceManagerCallbackClass, "sendFeatureReport", "(I[B)I" );
-		if ( !g_midHIDDeviceManagerSendFeatureReport )
-		{
-			__android_log_print(ANDROID_LOG_ERROR, TAG, "HIDDeviceRegisterCallback: callback class missing sendFeatureReport" );
-		}
-		g_midHIDDeviceManagerGetFeatureReport = env->GetMethodID( g_HIDDeviceManagerCallbackClass, "getFeatureReport", "(I[B)Z" );
-		if ( !g_midHIDDeviceManagerGetFeatureReport )
-		{
-			__android_log_print(ANDROID_LOG_ERROR, TAG, "HIDDeviceRegisterCallback: callback class missing getFeatureReport" );
-		}
-		g_midHIDDeviceManagerClose = env->GetMethodID( g_HIDDeviceManagerCallbackClass, "closeDevice", "(I)V" );
-		if ( !g_midHIDDeviceManagerClose )
-		{
-			__android_log_print(ANDROID_LOG_ERROR, TAG, "HIDDeviceRegisterCallback: callback class missing closeDevice" );
-		}
-		env->DeleteLocalRef( objClass );
-	}
-}
-
-extern "C"
-JNIEXPORT void JNICALL HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceReleaseCallback)(JNIEnv *env, jobject thiz)
-{
-	LOGV("HIDDeviceReleaseCallback");
-	if ( env->IsSameObject( thiz, g_HIDDeviceManagerCallbackHandler ) )
-	{
-		env->DeleteGlobalRef( g_HIDDeviceManagerCallbackClass );
-		g_HIDDeviceManagerCallbackClass = NULL;
-		env->DeleteGlobalRef( g_HIDDeviceManagerCallbackHandler );
-		g_HIDDeviceManagerCallbackHandler = NULL;
-		g_initialized = false;
-	}
-}
-
-extern "C"
-JNIEXPORT void JNICALL HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceConnected)(JNIEnv *env, jobject thiz, int nDeviceID, jstring sIdentifier, int nVendorId, int nProductId, jstring sSerialNumber, int nReleaseNumber, jstring sManufacturer, jstring sProduct, int nInterface, int nInterfaceClass, int nInterfaceSubclass, int nInterfaceProtocol )
-{
-	LOGV( "HIDDeviceConnected() id=%d VID/PID = %.4x/%.4x, interface %d\n", nDeviceID, nVendorId, nProductId, nInterface );
-
-	hid_device_info *pInfo = new hid_device_info;
-	SDL_memset( pInfo, 0, sizeof( *pInfo ) );
-	pInfo->path = CreateStringFromJString( env, sIdentifier );
-	pInfo->vendor_id = nVendorId;
-	pInfo->product_id = nProductId;
-	pInfo->serial_number = CreateWStringFromJString( env, sSerialNumber );
-	pInfo->release_number = nReleaseNumber;
-	pInfo->manufacturer_string = CreateWStringFromJString( env, sManufacturer );
-	pInfo->product_string = CreateWStringFromJString( env, sProduct );
-	pInfo->interface_number = nInterface;
-	pInfo->interface_class = nInterfaceClass;
-	pInfo->interface_subclass = nInterfaceSubclass;
-	pInfo->interface_protocol = nInterfaceProtocol;
-
-	hid_device_ref<CHIDDevice> pDevice( new CHIDDevice( nDeviceID, pInfo ) );
-
-	hid_mutex_guard l( &g_DevicesMutex );
-	hid_device_ref<CHIDDevice> pLast, pCurr;
-	for ( pCurr = g_Devices; pCurr; pLast = pCurr, pCurr = pCurr->next )
-	{
-		continue;
-	}
-	if ( pLast )
-	{
-		pLast->next = pDevice;
-	}
-	else
-	{
-		g_Devices = pDevice;
-	}
-}
-
-extern "C"
-JNIEXPORT void JNICALL HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceOpenPending)(JNIEnv *env, jobject thiz, int nDeviceID)
-{
-	LOGV( "HIDDeviceOpenPending() id=%d\n", nDeviceID );
-	hid_device_ref<CHIDDevice> pDevice = FindDevice( nDeviceID );
-	if ( pDevice )
-	{
-		pDevice->SetOpenPending();
-	}
-}
-
-extern "C"
-JNIEXPORT void JNICALL HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceOpenResult)(JNIEnv *env, jobject thiz, int nDeviceID, bool bOpened)
-{
-	LOGV( "HIDDeviceOpenResult() id=%d, result=%s\n", nDeviceID, bOpened ? "true" : "false" );
-	hid_device_ref<CHIDDevice> pDevice = FindDevice( nDeviceID );
-	if ( pDevice )
-	{
-		pDevice->SetOpenResult( bOpened );
-	}
-}
-
-extern "C"
-JNIEXPORT void JNICALL HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceDisconnected)(JNIEnv *env, jobject thiz, int nDeviceID)
-{
-	LOGV( "HIDDeviceDisconnected() id=%d\n", nDeviceID );
-	hid_device_ref<CHIDDevice> pDevice;
-	{
-		hid_mutex_guard l( &g_DevicesMutex );
-		hid_device_ref<CHIDDevice> pLast, pCurr;
-		for ( pCurr = g_Devices; pCurr; pLast = pCurr, pCurr = pCurr->next )
-		{
-			if ( pCurr->GetId() == nDeviceID )
-			{
-				pDevice = pCurr;
-
-				if ( pLast )
-				{
-					pLast->next = pCurr->next;
-				}
-				else
-				{
-					g_Devices = pCurr->next;
-				}
-			}
-		}
-	}
-	if ( pDevice )
-	{
-		pDevice->Close( false );
-	}
-}
-
-extern "C"
-JNIEXPORT void JNICALL HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceInputReport)(JNIEnv *env, jobject thiz, int nDeviceID, jbyteArray value)
-{
-	jbyte *pBuf = env->GetByteArrayElements(value, NULL);
-	jsize nBufSize = env->GetArrayLength(value);
-
-//	LOGV( "HIDDeviceInput() id=%d len=%u\n", nDeviceID, nBufSize );
-	hid_device_ref<CHIDDevice> pDevice = FindDevice( nDeviceID );
-	if ( pDevice )
-	{
-		pDevice->ProcessInput( reinterpret_cast< const uint8_t* >( pBuf ), nBufSize );
-	}
-
-	env->ReleaseByteArrayElements(value, pBuf, 0);
-}
-
-extern "C"
-JNIEXPORT void JNICALL HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceFeatureReport)(JNIEnv *env, jobject thiz, int nDeviceID, jbyteArray value)
-{
-	jbyte *pBuf = env->GetByteArrayElements(value, NULL);
-	jsize nBufSize = env->GetArrayLength(value);
-
-	LOGV( "HIDDeviceFeatureReport() id=%d len=%u\n", nDeviceID, nBufSize );
-	hid_device_ref<CHIDDevice> pDevice = FindDevice( nDeviceID );
-	if ( pDevice )
-	{
-		pDevice->ProcessFeatureReport( reinterpret_cast< const uint8_t* >( pBuf ), nBufSize );
-	}
-
-	env->ReleaseByteArrayElements(value, pBuf, 0);
-}
-
-//////////////////////////////////////////////////////////////////////////////////////////////////////////////
-//////////////////////////////////////////////////////////////////////////////////////////////////////////////
-//////////////////////////////////////////////////////////////////////////////////////////////////////////////
-
-extern "C"
-{
-
-int hid_init(void)
-{
-	if ( !g_initialized )
-	{
-		// HIDAPI doesn't work well with Android < 4.3
-		if (SDL_GetAndroidSDKVersion() >= 18) {
-			// Make sure thread is attached to JVM/env
-			JNIEnv *env;
-			g_JVM->AttachCurrentThread( &env, NULL );
-			pthread_setspecific( g_ThreadKey, (void*)env );
-
-			if ( !g_HIDDeviceManagerCallbackHandler )
-			{
-				LOGV( "hid_init() without callback handler" );
-				return -1;
-			}
-
-			// Bluetooth is currently only used for Steam Controllers, so check that hint
-			// before initializing Bluetooth, which will prompt the user for permission.
-			bool init_usb = true;
-			bool init_bluetooth = false;
-			if (SDL_GetHintBoolean(SDL_HINT_JOYSTICK_HIDAPI_STEAM, SDL_FALSE)) {
-				if (SDL_GetAndroidSDKVersion() < 31 ||
-					Android_JNI_RequestPermission("android.permission.BLUETOOTH_CONNECT")) {
-					init_bluetooth = true;
-				}
-			}
-			env->CallBooleanMethod( g_HIDDeviceManagerCallbackHandler, g_midHIDDeviceManagerInitialize, init_usb, init_bluetooth );
-			ExceptionCheck( env, NULL, "hid_init" );
-		}
-		g_initialized = true;	// Regardless of result, so it's only called once
-	}
-	return 0;
-}
-
-struct hid_device_info HID_API_EXPORT * HID_API_CALL hid_enumerate(unsigned short vendor_id, unsigned short product_id)
-{
-	struct hid_device_info *root = NULL;
-	const char *hint = SDL_GetHint(SDL_HINT_HIDAPI_IGNORE_DEVICES);
-
-	hid_mutex_guard l( &g_DevicesMutex );
-	for ( hid_device_ref<CHIDDevice> pDevice = g_Devices; pDevice; pDevice = pDevice->next )
-	{
-		const hid_device_info *info = pDevice->GetDeviceInfo();
-
-		/* See if there are any devices we should skip in enumeration */
-		if (hint) {
-			char vendor_match[16], product_match[16];
-			SDL_snprintf(vendor_match, sizeof(vendor_match), "0x%.4x/0x0000", info->vendor_id);
-			SDL_snprintf(product_match, sizeof(product_match), "0x%.4x/0x%.4x", info->vendor_id, info->product_id);
-			if (SDL_strcasestr(hint, vendor_match) || SDL_strcasestr(hint, product_match)) {
-				continue;
-			}
-		}
-
-		if ( ( vendor_id == 0x0 || info->vendor_id == vendor_id ) &&
-		     ( product_id == 0x0 || info->product_id == product_id ) )
-		{
-			hid_device_info *dev = CopyHIDDeviceInfo( info );
-			dev->next = root;
-			root = dev;
-		}
-	}
-	return root;
-}
-
-void  HID_API_EXPORT HID_API_CALL hid_free_enumeration(struct hid_device_info *devs)
-{
-	while ( devs )
-	{
-		struct hid_device_info *next = devs->next;
-		FreeHIDDeviceInfo( devs );
-		devs = next;
-	}
-}
-
-HID_API_EXPORT hid_device * HID_API_CALL hid_open(unsigned short vendor_id, unsigned short product_id, const wchar_t *serial_number)
-{
-	// TODO: Implement
-	return NULL;
-}
-
-HID_API_EXPORT hid_device * HID_API_CALL hid_open_path(const char *path, int bExclusive)
-{
-	LOGV( "hid_open_path( %s )", path );
-
-	hid_device_ref< CHIDDevice > pDevice;
-	{
-		hid_mutex_guard r( &g_DevicesRefCountMutex );
-		hid_mutex_guard l( &g_DevicesMutex );
-		for ( hid_device_ref<CHIDDevice> pCurr = g_Devices; pCurr; pCurr = pCurr->next )
-		{
-			if ( SDL_strcmp( pCurr->GetDeviceInfo()->path, path ) == 0 )
-			{
-				hid_device *pValue = pCurr->GetDevice();
-				if ( pValue )
-				{
-					++pValue->m_nDeviceRefCount;
-					LOGD("Incrementing device %d (%p), refCount = %d\n", pValue->m_nId, pValue, pValue->m_nDeviceRefCount);
-					return pValue;
-				}
-
-				// Hold a shared pointer to the controller for the duration
-				pDevice = pCurr;
-				break;
-			}
-		}
-	}
-	if ( pDevice && pDevice->BOpen() )
-	{
-		return pDevice->GetDevice();
-	}
-	return NULL;
-}
-
-int  HID_API_EXPORT HID_API_CALL hid_write(hid_device *device, const unsigned char *data, size_t length)
-{
-	if ( device )
-	{
-		LOGV( "hid_write id=%d length=%u", device->m_nId, length );
-		hid_device_ref<CHIDDevice> pDevice = FindDevice( device->m_nId );
-		if ( pDevice )
-		{
-			return pDevice->SendOutputReport( data, length );
-		}
-	}
-	return -1; // Controller was disconnected
-}
-
-static uint32_t getms()
-{
-	struct timeval now;
-
-	gettimeofday(&now, NULL);
-	return (uint32_t)(now.tv_sec * 1000 + now.tv_usec / 1000);
-}
-
-static void delayms(uint32_t ms)
-{
-    int was_error;
-
-    struct timespec elapsed, tv;
-
-    /* Set the timeout interval */
-    elapsed.tv_sec = ms / 1000;
-    elapsed.tv_nsec = (ms % 1000) * 1000000;
-    do {
-        errno = 0;
-
-        tv.tv_sec = elapsed.tv_sec;
-        tv.tv_nsec = elapsed.tv_nsec;
-        was_error = nanosleep(&tv, &elapsed);
-    } while (was_error && (errno == EINTR));
-}
-
-int HID_API_EXPORT HID_API_CALL hid_read_timeout(hid_device *device, unsigned char *data, size_t length, int milliseconds)
-{
-	if ( device )
-	{
-//		LOGV( "hid_read_timeout id=%d length=%u timeout=%d", device->m_nId, length, milliseconds );
-		hid_device_ref<CHIDDevice> pDevice = FindDevice( device->m_nId );
-		if ( pDevice )
-		{
-			int nResult = pDevice->GetInput( data, length );
-			if ( nResult == 0 && milliseconds > 0 )
-			{
-				uint32_t start = getms();
-				do
-				{
-					delayms( 1 );
-					nResult = pDevice->GetInput( data, length );
-				} while ( nResult == 0 && ( getms() - start ) < milliseconds );
-			}
-			return nResult;
-		}
-		LOGV( "controller was disconnected" );
-	}
-	return -1; // Controller was disconnected
-}
-
-// TODO: Implement blocking
-int  HID_API_EXPORT HID_API_CALL hid_read(hid_device *device, unsigned char *data, size_t length)
-{
-	LOGV( "hid_read id=%d length=%u", device->m_nId, length );
-	return hid_read_timeout( device, data, length, 0 );
-}
-
-// TODO: Implement?
-int  HID_API_EXPORT HID_API_CALL hid_set_nonblocking(hid_device *device, int nonblock)
-{
-	return -1;
-}
-
-int HID_API_EXPORT HID_API_CALL hid_send_feature_report(hid_device *device, const unsigned char *data, size_t length)
-{
-	if ( device )
-	{
-		LOGV( "hid_send_feature_report id=%d length=%u", device->m_nId, length );
-		hid_device_ref<CHIDDevice> pDevice = FindDevice( device->m_nId );
-		if ( pDevice )
-		{
-			return pDevice->SendFeatureReport( data, length );
-		}
-	}
-	return -1; // Controller was disconnected
-}
-
-
-// Synchronous operation. Will block until completed.
-int HID_API_EXPORT HID_API_CALL hid_get_feature_report(hid_device *device, unsigned char *data, size_t length)
-{
-	if ( device )
-	{
-		LOGV( "hid_get_feature_report id=%d length=%u", device->m_nId, length );
-		hid_device_ref<CHIDDevice> pDevice = FindDevice( device->m_nId );
-		if ( pDevice )
-		{
-			return pDevice->GetFeatureReport( data, length );
-		}
-	}
-	return -1; // Controller was disconnected
-}
-
-
-void HID_API_EXPORT HID_API_CALL hid_close(hid_device *device)
-{
-	if ( device )
-	{
-		LOGV( "hid_close id=%d", device->m_nId );
-		hid_mutex_guard r( &g_DevicesRefCountMutex );
-		LOGD("Decrementing device %d (%p), refCount = %d\n", device->m_nId, device, device->m_nDeviceRefCount - 1);
-		if ( --device->m_nDeviceRefCount == 0 )
-		{
-			hid_device_ref<CHIDDevice> pDevice = FindDevice( device->m_nId );
-			if ( pDevice )
-			{
-				pDevice->Close( true );
-			}
-			else
-			{
-				delete device;
-			}
-			LOGD("Deleted device %p\n", device);
-		}
-	}
-}
-
-int HID_API_EXPORT_CALL hid_get_manufacturer_string(hid_device *device, wchar_t *string, size_t maxlen)
-{
-	if ( device )
-	{
-		hid_device_ref<CHIDDevice> pDevice = FindDevice( device->m_nId );
-		if ( pDevice )
-		{
-			wcsncpy( string, pDevice->GetDeviceInfo()->manufacturer_string, maxlen );
-			return 0;
-		}
-	}
-	return -1;
-}
-
-int HID_API_EXPORT_CALL hid_get_product_string(hid_device *device, wchar_t *string, size_t maxlen)
-{
-	if ( device )
-	{
-		hid_device_ref<CHIDDevice> pDevice = FindDevice( device->m_nId );
-		if ( pDevice )
-		{
-			wcsncpy( string, pDevice->GetDeviceInfo()->product_string, maxlen );
-			return 0;
-		}
-	}
-	return -1;
-}
-
-int HID_API_EXPORT_CALL hid_get_serial_number_string(hid_device *device, wchar_t *string, size_t maxlen)
-{
-	if ( device )
-	{
-		hid_device_ref<CHIDDevice> pDevice = FindDevice( device->m_nId );
-		if ( pDevice )
-		{
-			wcsncpy( string, pDevice->GetDeviceInfo()->serial_number, maxlen );
-			return 0;
-		}
-	}
-	return -1;
-}
-
-int HID_API_EXPORT_CALL hid_get_indexed_string(hid_device *device, int string_index, wchar_t *string, size_t maxlen)
-{
-	return -1;
-}
-
-HID_API_EXPORT const wchar_t* HID_API_CALL hid_error(hid_device *device)
-{
-	return NULL;
-}
-
-int hid_exit(void)
-{
-	return 0;
-}
-
-}
-
-#else
-
-extern "C"
-JNIEXPORT void JNICALL HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceRegisterCallback)(JNIEnv *env, jobject thiz);
-
-extern "C"
-JNIEXPORT void JNICALL HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceReleaseCallback)(JNIEnv *env, jobject thiz);
-
-extern "C"
-JNIEXPORT void JNICALL HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceConnected)(JNIEnv *env, jobject thiz, int nDeviceID, jstring sIdentifier, int nVendorId, int nProductId, jstring sSerialNumber, int nReleaseNumber, jstring sManufacturer, jstring sProduct, int nInterface, int nInterfaceClass, int nInterfaceSubclass, int nInterfaceProtocol );
-
-extern "C"
-JNIEXPORT void JNICALL HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceOpenPending)(JNIEnv *env, jobject thiz, int nDeviceID);
-
-extern "C"
-JNIEXPORT void JNICALL HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceOpenResult)(JNIEnv *env, jobject thiz, int nDeviceID, bool bOpened);
-
-extern "C"
-JNIEXPORT void JNICALL HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceDisconnected)(JNIEnv *env, jobject thiz, int nDeviceID);
-
-extern "C"
-JNIEXPORT void JNICALL HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceInputReport)(JNIEnv *env, jobject thiz, int nDeviceID, jbyteArray value);
-
-extern "C"
-JNIEXPORT void JNICALL HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceFeatureReport)(JNIEnv *env, jobject thiz, int nDeviceID, jbyteArray value);
-
-
-extern "C"
-JNIEXPORT void JNICALL HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceRegisterCallback)(JNIEnv *env, jobject thiz )
-{
-	LOGV("Stub HIDDeviceRegisterCallback()");
-}
-
-extern "C"
-JNIEXPORT void JNICALL HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceReleaseCallback)(JNIEnv *env, jobject thiz)
-{
-	LOGV("Stub HIDDeviceReleaseCallback()");
-}
-
-extern "C"
-JNIEXPORT void JNICALL HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceConnected)(JNIEnv *env, jobject thiz, int nDeviceID, jstring sIdentifier, int nVendorId, int nProductId, jstring sSerialNumber, int nReleaseNumber, jstring sManufacturer, jstring sProduct, int nInterface, int nInterfaceClass, int nInterfaceSubclass, int nInterfaceProtocol )
-{
-	LOGV("Stub HIDDeviceConnected() id=%d VID/PID = %.4x/%.4x, interface %d\n", nDeviceID, nVendorId, nProductId, nInterface);
-}
-
-extern "C"
-JNIEXPORT void JNICALL HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceOpenPending)(JNIEnv *env, jobject thiz, int nDeviceID)
-{
-	LOGV("Stub HIDDeviceOpenPending() id=%d\n", nDeviceID);
-}
-
-extern "C"
-JNIEXPORT void JNICALL HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceOpenResult)(JNIEnv *env, jobject thiz, int nDeviceID, bool bOpened)
-{
-	LOGV("Stub HIDDeviceOpenResult() id=%d, result=%s\n", nDeviceID, bOpened ? "true" : "false");
-}
-
-extern "C"
-JNIEXPORT void JNICALL HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceDisconnected)(JNIEnv *env, jobject thiz, int nDeviceID)
-{
-	LOGV("Stub HIDDeviceDisconnected() id=%d\n", nDeviceID);
-}
-
-extern "C"
-JNIEXPORT void JNICALL HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceInputReport)(JNIEnv *env, jobject thiz, int nDeviceID, jbyteArray value)
-{
-	LOGV("Stub HIDDeviceInput() id=%d len=%u\n", nDeviceID, nBufSize);
-}
-
-extern "C"
-JNIEXPORT void JNICALL HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceFeatureReport)(JNIEnv *env, jobject thiz, int nDeviceID, jbyteArray value)
-{
-	LOGV("Stub HIDDeviceFeatureReport() id=%d len=%u\n", nDeviceID, nBufSize);
-}
-
-#endif /* SDL_HIDAPI_DISABLED */
-
-extern "C"
-JNINativeMethod HIDDeviceManager_tab[8] = {
-        { "HIDDeviceRegisterCallback", "()V", (void*)HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceRegisterCallback) },
-        { "HIDDeviceReleaseCallback", "()V", (void*)HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceReleaseCallback) },
-        { "HIDDeviceConnected", "(ILjava/lang/String;IILjava/lang/String;ILjava/lang/String;Ljava/lang/String;IIII)V", (void*)HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceConnected) },
-        { "HIDDeviceOpenPending", "(I)V", (void*)HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceOpenPending) },
-        { "HIDDeviceOpenResult", "(IZ)V", (void*)HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceOpenResult) },
-        { "HIDDeviceDisconnected", "(I)V", (void*)HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceDisconnected) },
-        { "HIDDeviceInputReport", "(I[B)V", (void*)HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceInputReport) },
-        { "HIDDeviceFeatureReport", "(I[B)V", (void*)HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceFeatureReport) }
-};

+ 0 - 39
src/hidapi/android/hid.h

@@ -1,39 +0,0 @@
-/*
-  Simple DirectMedia Layer
-  Copyright (C) 2022 Valve Corporation
-
-  This software is provided 'as-is', without any express or implied
-  warranty.  In no event will the authors be held liable for any damages
-  arising from the use of this software.
-
-  Permission is granted to anyone to use this software for any purpose,
-  including commercial applications, and to alter it and redistribute it
-  freely, subject to the following restrictions:
-
-  1. The origin of this software must not be misrepresented; you must not
-     claim that you wrote the original software. If you use this software
-     in a product, an acknowledgment in the product documentation would be
-     appreciated but is not required.
-  2. Altered source versions must be plainly marked as such, and must not be
-     misrepresented as being the original software.
-  3. This notice may not be removed or altered from any source distribution.
-*/
-
-// Purpose: Exporting table containing HIDDeviceManager native methods
-
-#ifndef SDL_android_hid_h_
-#define SDL_android_hid_h_
-
-#include <jni.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-extern JNINativeMethod HIDDeviceManager_tab[8];
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif

+ 0 - 16
src/hidapi/android/jni/Android.mk

@@ -1,16 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-
-HIDAPI_ROOT_REL:= ../..
-HIDAPI_ROOT_ABS:= $(LOCAL_PATH)/../..
-
-include $(CLEAR_VARS)
-
-LOCAL_CPPFLAGS += -std=c++11
-
-LOCAL_SRC_FILES := \
-  $(HIDAPI_ROOT_REL)/android/hid.cpp
-
-LOCAL_MODULE := libhidapi
-LOCAL_LDLIBS := -llog
-
-include $(BUILD_SHARED_LIBRARY)

+ 0 - 2
src/hidapi/android/jni/Application.mk

@@ -1,2 +0,0 @@
-APP_STL := gnustl_static
-APP_ABI := armeabi-v7a

+ 0 - 14
src/hidapi/android/project.properties

@@ -1,14 +0,0 @@
-# This file is automatically generated by Android Tools.
-# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
-#
-# This file must be checked in Version Control Systems.
-#
-# To customize properties used by the Ant build system edit
-# "ant.properties", and override values to adapt the script to your
-# project structure.
-#
-# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):
-#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
-
-# Project target.
-target=android-21

+ 44 - 24
src/hidapi/configure.ac

@@ -1,13 +1,9 @@
 AC_PREREQ(2.63)
 
-# Version number. This is currently the only place.
-m4_define([HIDAPI_MAJOR],   0)
-m4_define([HIDAPI_MINOR],   8)
-m4_define([HIDAPI_RELEASE], 0)
-m4_define([HIDAPI_RC],      -rc1)
-m4_define([VERSION_STRING], HIDAPI_MAJOR[.]HIDAPI_MINOR[.]HIDAPI_RELEASE[]HIDAPI_RC)
+AC_INIT([hidapi],[m4_normalize(m4_builtin([include], VERSION))],[https://github.com/libusb/hidapi/issues])
 
-AC_INIT([hidapi],[VERSION_STRING],[[email protected]])
+echo "This build script for HIDAPI is deprecated."
+echo "Consider using CMake instead."
 
 # Library soname version
 # Follow the following rules (particularly the ones in the second link):
@@ -20,7 +16,6 @@ LTLDFLAGS="-version-info ${lt_current}:${lt_revision}:${lt_age}"
 
 AC_CONFIG_MACRO_DIR([m4])
 AM_INIT_AUTOMAKE([foreign -Wall -Werror])
-AC_CONFIG_MACRO_DIR([m4])
 
 m4_ifdef([AM_PROG_AR], [AM_PROG_AR])
 LT_INIT
@@ -63,14 +58,14 @@ case $host in
 
 	# HIDAPI/hidraw libs
 	PKG_CHECK_MODULES([libudev], [libudev], true, [hidapi_lib_error libudev])
-	LIBS_HIDRAW_PR+=" $libudev_LIBS"
-	CFLAGS_HIDRAW+=" $libudev_CFLAGS"
+	LIBS_HIDRAW_PR="${LIBS_HIDRAW_PR} $libudev_LIBS"
+	CFLAGS_HIDRAW="${CFLAGS_HIDRAW} $libudev_CFLAGS"
 
 	# HIDAPI/libusb libs
-	AC_CHECK_LIB([rt], [clock_gettime], [LIBS_LIBUSB_PRIVATE+=" -lrt"], [hidapi_lib_error librt])
+	AC_CHECK_LIB([rt], [clock_gettime], [LIBS_LIBUSB_PRIVATE="${LIBS_LIBUSB_PRIVATE} -lrt"], [hidapi_lib_error librt])
 	PKG_CHECK_MODULES([libusb], [libusb-1.0 >= 1.0.9], true, [hidapi_lib_error libusb-1.0])
-	LIBS_LIBUSB_PRIVATE+=" $libusb_LIBS"
-	CFLAGS_LIBUSB+=" $libusb_CFLAGS"
+	LIBS_LIBUSB_PRIVATE="${LIBS_LIBUSB_PRIVATE} $libusb_LIBS"
+	CFLAGS_LIBUSB="${CFLAGS_LIBUSB} $libusb_CFLAGS"
 	;;
 *-darwin*)
 	AC_MSG_RESULT([ (Mac OS X back-end)])
@@ -79,7 +74,7 @@ case $host in
 	backend="mac"
 	os="darwin"
 	threads="pthreads"
-	LIBS="${LIBS} -framework IOKit -framework CoreFoundation"
+	LIBS="${LIBS} -framework IOKit -framework CoreFoundation -framework AppKit"
 	;;
 *-freebsd*)
 	AC_MSG_RESULT([ (FreeBSD back-end)])
@@ -92,9 +87,10 @@ case $host in
 	CFLAGS="$CFLAGS -I/usr/local/include"
 	LDFLAGS="$LDFLAGS -L/usr/local/lib"
 	LIBS="${LIBS}"
-	AC_CHECK_LIB([usb], [libusb_init], [LIBS_LIBUSB_PRIVATE="${LIBS_LIBUSB_PRIVATE} -lusb"], [hidapi_lib_error libusb])
+	PKG_CHECK_MODULES([libusb], [libusb-1.0 >= 1.0.9], true, [hidapi_lib_error libusb-1.0])
+	LIBS_LIBUSB_PRIVATE="${LIBS_LIBUSB_PRIVATE} $libusb_LIBS"
+	CFLAGS_LIBUSB="${CFLAGS_LIBUSB} $libusb_CFLAGS"
 	AC_CHECK_LIB([iconv], [iconv_open], [LIBS_LIBUSB_PRIVATE="${LIBS_LIBUSB_PRIVATE} -liconv"], [hidapi_lib_error libiconv])
-	echo libs_priv: $LIBS_LIBUSB_PRIVATE
 	;;
 *-kfreebsd*)
 	AC_MSG_RESULT([ (kFreeBSD back-end)])
@@ -104,8 +100,22 @@ case $host in
 	os="kfreebsd"
 	threads="pthreads"
 
-	AC_CHECK_LIB([usb], [libusb_init], [LIBS_LIBUSB_PRIVATE="${LIBS_LIBUSB_PRIVATE} -lusb"], [hidapi_lib_error libusb])
-	echo libs_priv: $LIBS_LIBUSB_PRIVATE
+	PKG_CHECK_MODULES([libusb], [libusb-1.0 >= 1.0.9], true, [hidapi_lib_error libusb-1.0])
+	LIBS_LIBUSB_PRIVATE="${LIBS_LIBUSB_PRIVATE} $libusb_LIBS"
+	CFLAGS_LIBUSB="${CFLAGS_LIBUSB} $libusb_CFLAGS"
+	;;
+*-*-haiku)
+	AC_MSG_RESULT([ (Haiku back-end)])
+	AC_DEFINE(OS_HAIKU, 1, [Haiku implementation])
+	AC_SUBST(OS_HAIKU)
+	backend="libusb"
+	os="haiku"
+	threads="pthreads"
+
+	PKG_CHECK_MODULES([libusb], [libusb-1.0 >= 1.0.9], true, [hidapi_lib_error libusb-1.0])
+	LIBS_LIBUSB_PRIVATE="${LIBS_LIBUSB_PRIVATE} $libusb_LIBS"
+	CFLAGS_LIBUSB="${CFLAGS_LIBUSB} $libusb_CFLAGS"
+	AC_CHECK_LIB([iconv], [libiconv_open], [LIBS_LIBUSB_PRIVATE="${LIBS_LIBUSB_PRIVATE} -liconv"], [hidapi_lib_error libiconv])
 	;;
 *-mingw*)
 	AC_MSG_RESULT([ (Windows back-end, using MinGW)])
@@ -113,6 +123,15 @@ case $host in
 	os="windows"
 	threads="windows"
 	win_implementation="mingw"
+	LDFLAGS="${LDFLAGS} -static-libgcc"
+	;;
+*-msys*)
+	AC_MSG_RESULT([ (Windows back-end, using MSYS2)])
+	backend="windows"
+	os="windows"
+	threads="windows"
+	win_implementation="mingw"
+	LDFLAGS="${LDFLAGS} -static-libgcc"
 	;;
 *-cygwin*)
 	AC_MSG_RESULT([ (Windows back-end, using Cygwin)])
@@ -136,7 +155,7 @@ if test "x$os" = xwindows; then
 	AC_DEFINE(OS_WINDOWS, 1, [Windows implementations])
 	AC_SUBST(OS_WINDOWS)
 	LDFLAGS="${LDFLAGS} -no-undefined"
-	LIBS="${LIBS} -lsetupapi"
+	LIBS="${LIBS}"
 fi
 
 if test "x$threads" = xpthreads; then
@@ -175,15 +194,15 @@ mkdir testgui/TestGUI.app/Contents/MacOS/
 
 if test "x$testgui_enabled" != "xno"; then
 	if test "x$os" = xdarwin; then
-		# On Mac OS, don't use pkg-config.
+		# On Mac OS, do not use pkg-config.
 		AC_CHECK_PROG([foxconfig], [fox-config], [fox-config], false)
 		if test "x$foxconfig" = "xfalse"; then
 			hidapi_prog_error fox-config "FOX Toolkit"
 		fi
-		LIBS_TESTGUI+=`$foxconfig --libs`
-		LIBS_TESTGUI+=" -framework Cocoa -L/usr/X11R6/lib"
-		CFLAGS_TESTGUI+=`$foxconfig --cflags`
-		OBJCFLAGS+=" -x objective-c++"
+		LIBS_TESTGUI="${LIBS_TESTGUI} `$foxconfig --libs`"
+		LIBS_TESTGUI="${LIBS_TESTGUI} -framework Cocoa -L/usr/X11R6/lib"
+		CFLAGS_TESTGUI="${CFLAGS_TESTGUI} `$foxconfig --cflags`"
+		OBJCFLAGS="${OBJCFLAGS} -x objective-c++"
 	elif test "x$os" = xwindows; then
 		# On Windows, just set the paths for Fox toolkit
 		if test "x$win_implementation" = xmingw; then
@@ -213,6 +232,7 @@ AM_CONDITIONAL(OS_LINUX, test "x$os" = xlinux)
 AM_CONDITIONAL(OS_DARWIN, test "x$os" = xdarwin)
 AM_CONDITIONAL(OS_FREEBSD, test "x$os" = xfreebsd)
 AM_CONDITIONAL(OS_KFREEBSD, test "x$os" = xkfreebsd)
+AM_CONDITIONAL(OS_HAIKU, test "x$os" = xhaiku)
 AM_CONDITIONAL(OS_WINDOWS, test "x$os" = xwindows)
 
 AC_CONFIG_HEADERS([config.h])

+ 31 - 0
src/hidapi/dist/hidapi.podspec

@@ -0,0 +1,31 @@
+Pod::Spec.new do |spec|
+
+  spec.name         = "hidapi"
+  spec.version      = File.read('../VERSION')
+  spec.summary      = "A Simple library for communicating with USB and Bluetooth HID devices on Linux, Mac and Windows."
+
+  spec.description  = <<-DESC
+  HIDAPI is a multi-platform library which allows an application to interface with USB and Bluetooth HID-Class devices on Windows, Linux, FreeBSD, and macOS. HIDAPI can be either built as a shared library (.so, .dll or .dylib) or can be embedded directly into a target application by adding a single source file (per platform) and a single header.
+                   DESC
+
+  spec.homepage     = "https://github.com/libusb/hidapi"
+
+  spec.license      = { :type=> "GNU GPLv3 or BSD or HIDAPI original", :file => "LICENSE.txt" }
+
+  spec.authors      = { "Alan Ott" => "[email protected]",
+                        "Ludovic Rousseau" => "[email protected]",
+                        "libusb/hidapi Team" => "https://github.com/libusb/hidapi/blob/master/AUTHORS.txt",
+                      }
+
+  spec.platform     = :osx
+  spec.osx.deployment_target = "10.7"
+
+  spec.source       = { :git => "https://github.com/libusb/hidapi.git", :tag => "hidapi-#{spec.version}" }
+
+  spec.source_files = "mac/hid.c", "hidapi/hidapi.h", "mac/hidapi_darwin.h"
+
+  spec.public_header_files = "hidapi/hidapi.h", "mac/hidapi_darwin.h"
+
+  spec.frameworks   = "IOKit", "CoreFoundation", "AppKit"
+
+end

BIN
src/hidapi/documentation/cmake-gui-drop-down.png


BIN
src/hidapi/documentation/cmake-gui-highlights.png


File diff suppressed because it is too large
+ 682 - 327
src/hidapi/doxygen/Doxyfile


+ 13 - 0
src/hidapi/doxygen/main_page.md

@@ -0,0 +1,13 @@
+# 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.

+ 261 - 58
src/hidapi/hidapi/hidapi.h

@@ -5,9 +5,9 @@
  Alan Ott
  Signal 11 Software
 
- 8/22/2009
+ libusb/hidapi Team
 
- Copyright 2009, All Rights Reserved.
+ Copyright 2023, All Rights Reserved.
 
  At the discretion of the user of this library,
  this software may be licensed under the terms of the
@@ -29,36 +29,123 @@
 
 #include <wchar.h>
 
-#ifdef SDL_hidapi_h_
-#define SDL_HIDAPI_IMPLEMENTATION
-#define hid_device_info SDL_hid_device_info
-#endif
-
-#if defined(_WIN32) && !defined(NAMESPACE) && !defined(SDL_HIDAPI_IMPLEMENTATION) /* SDL: don't export hidapi syms */
+/* #480: this is to be refactored properly for v1.0 */
+#ifdef _WIN32
+   #ifndef HID_API_NO_EXPORT_DEFINE
       #define HID_API_EXPORT __declspec(dllexport)
-      #define HID_API_CALL
-#else
-#ifndef HID_API_EXPORT
-      #define HID_API_EXPORT /**< API export macro */
-#endif
-#ifndef HID_API_CALL
-      #define HID_API_CALL /**< API call macro */
+   #endif
 #endif
+#ifndef HID_API_EXPORT
+   #define HID_API_EXPORT /**< API export macro */
 #endif
+/* To be removed in v1.0 */
+#define HID_API_CALL /**< API call macro */
 
 #define HID_API_EXPORT_CALL HID_API_EXPORT HID_API_CALL /**< API export and call macro*/
 
-#if defined(__cplusplus) && !defined(NAMESPACE)
+/** @brief Static/compile-time major version of the library.
+
+	@ingroup API
+*/
+#define HID_API_VERSION_MAJOR 0
+/** @brief Static/compile-time minor version of the library.
+
+	@ingroup API
+*/
+#define HID_API_VERSION_MINOR 14
+/** @brief Static/compile-time patch version of the library.
+
+	@ingroup API
+*/
+#define HID_API_VERSION_PATCH 0
+
+/* Helper macros */
+#define HID_API_AS_STR_IMPL(x) #x
+#define HID_API_AS_STR(x) HID_API_AS_STR_IMPL(x)
+#define HID_API_TO_VERSION_STR(v1, v2, v3) HID_API_AS_STR(v1.v2.v3)
+
+/** @brief Coverts a version as Major/Minor/Patch into a number:
+	<8 bit major><16 bit minor><8 bit patch>.
+
+	This macro was added in version 0.12.0.
+
+	Convenient function to be used for compile-time checks, like:
+	@code{.c}
+	#if HID_API_VERSION >= HID_API_MAKE_VERSION(0, 12, 0)
+	@endcode
+
+	@ingroup API
+*/
+#define HID_API_MAKE_VERSION(mj, mn, p) (((mj) << 24) | ((mn) << 8) | (p))
+
+/** @brief Static/compile-time version of the library.
+
+	This macro was added in version 0.12.0.
+
+	@see @ref HID_API_MAKE_VERSION.
+
+	@ingroup API
+*/
+#define HID_API_VERSION HID_API_MAKE_VERSION(HID_API_VERSION_MAJOR, HID_API_VERSION_MINOR, HID_API_VERSION_PATCH)
+
+/** @brief Static/compile-time string version of the library.
+
+	@ingroup API
+*/
+#define HID_API_VERSION_STR HID_API_TO_VERSION_STR(HID_API_VERSION_MAJOR, HID_API_VERSION_MINOR, HID_API_VERSION_PATCH)
+
+/** @brief Maximum expected HID Report descriptor size in bytes.
+
+	Since version 0.13.0, @ref HID_API_VERSION >= HID_API_MAKE_VERSION(0, 13, 0)
+
+	@ingroup API
+*/
+#define HID_API_MAX_REPORT_DESCRIPTOR_SIZE 4096
+
+#ifdef __cplusplus
 extern "C" {
 #endif
-#ifdef NAMESPACE
-namespace NAMESPACE {
-#endif
+		/** A structure to hold the version numbers. */
+		struct hid_api_version {
+			int major; /**< major version number */
+			int minor; /**< minor version number */
+			int patch; /**< patch version number */
+		};
 
 		struct hid_device_;
 		typedef struct hid_device_ hid_device; /**< opaque hidapi structure */
 
-#ifndef SDL_HIDAPI_IMPLEMENTATION
+		/** @brief HID underlying bus types.
+
+			@ingroup API
+		*/
+		typedef enum {
+			/** Unknown bus type */
+			HID_API_BUS_UNKNOWN = 0x00,
+
+			/** USB bus
+			   Specifications:
+			   https://usb.org/hid */
+			HID_API_BUS_USB = 0x01,
+
+			/** Bluetooth or Bluetooth LE bus
+			   Specifications:
+			   https://www.bluetooth.com/specifications/specs/human-interface-device-profile-1-1-1/
+			   https://www.bluetooth.com/specifications/specs/hid-service-1-0/
+			   https://www.bluetooth.com/specifications/specs/hid-over-gatt-profile-1-0/ */
+			HID_API_BUS_BLUETOOTH = 0x02,
+
+			/** I2C bus
+			   Specifications:
+			   https://docs.microsoft.com/previous-versions/windows/hardware/design/dn642101(v=vs.85) */
+			HID_API_BUS_I2C = 0x03,
+
+			/** SPI bus
+			   Specifications:
+			   https://www.microsoft.com/download/details.aspx?id=103325 */
+			HID_API_BUS_SPI = 0x04,
+		} hid_bus_type;
+
 		/** hidapi info structure */
 		struct hid_device_info {
 			/** Platform-specific device path */
@@ -77,29 +164,27 @@ namespace NAMESPACE {
 			/** Product string */
 			wchar_t *product_string;
 			/** Usage Page for this Device/Interface
-			    (Windows/Mac only). */
+			    (Windows/Mac/hidraw only) */
 			unsigned short usage_page;
 			/** Usage for this Device/Interface
-			    (Windows/Mac only).*/
+			    (Windows/Mac/hidraw only) */
 			unsigned short usage;
 			/** The USB interface which this logical device
 			    represents.
 
-				* Valid on both Linux implementations in all cases.
-				* Valid on the Windows implementation only if the device
-				  contains more than one interface. */
+			    Valid only if the device is a USB HID device.
+			    Set to -1 in all other cases.
+			*/
 			int interface_number;
 
-			/** Additional information about the USB interface.
-			    Valid on libusb and Android implementations. */
-			int interface_class;
-			int interface_subclass;
-			int interface_protocol;
-
 			/** Pointer to the next device */
 			struct hid_device_info *next;
+
+			/** Underlying bus type
+			    Since version 0.13.0, @ref HID_API_VERSION >= HID_API_MAKE_VERSION(0, 13, 0)
+			*/
+			hid_bus_type bus_type;
 		};
-#endif /* !SDL_HIDAPI_IMPLEMENTATION */
 
 
 		/** @brief Initialize the HIDAPI library.
@@ -115,6 +200,7 @@ namespace NAMESPACE {
 
 			@returns
 				This function returns 0 on success and -1 on error.
+				Call hid_error(NULL) to get the failure reason.
 		*/
 		int HID_API_EXPORT HID_API_CALL hid_init(void);
 
@@ -126,7 +212,7 @@ namespace NAMESPACE {
 
 			@ingroup API
 
-		    @returns
+			@returns
 				This function returns 0 on success and -1 on error.
 		*/
 		int HID_API_EXPORT HID_API_CALL hid_exit(void);
@@ -146,21 +232,25 @@ namespace NAMESPACE {
 			@param product_id The Product ID (PID) of the types of
 				device to open.
 
-		    @returns
-		    	This function returns a pointer to a linked list of type
-		    	struct #hid_device_info, containing information about the HID devices
-		    	attached to the system, or NULL in the case of failure. Free
-		    	this linked list by calling hid_free_enumeration().
+			@returns
+				This function returns a pointer to a linked list of type
+				struct #hid_device_info, containing information about the HID devices
+				attached to the system,
+				or NULL in the case of failure or if no HID devices present in the system.
+				Call hid_error(NULL) to get the failure reason.
+
+			@note The returned value by this function must to be freed by calling hid_free_enumeration(),
+			      when not needed anymore.
 		*/
 		struct hid_device_info HID_API_EXPORT * HID_API_CALL hid_enumerate(unsigned short vendor_id, unsigned short product_id);
 
 		/** @brief Free an enumeration Linked List
 
-		    This function frees a linked list created by hid_enumerate().
+			This function frees a linked list created by hid_enumerate().
 
 			@ingroup API
-		    @param devs Pointer to a list of struct_device returned from
-		    	      hid_enumerate().
+			@param devs Pointer to a list of struct_device returned from
+			            hid_enumerate().
 		*/
 		void  HID_API_EXPORT HID_API_CALL hid_free_enumeration(struct hid_device_info *devs);
 
@@ -174,11 +264,15 @@ namespace NAMESPACE {
 			@param vendor_id The Vendor ID (VID) of the device to open.
 			@param product_id The Product ID (PID) of the device to open.
 			@param serial_number The Serial Number of the device to open
-				               (Optionally NULL).
+			                     (Optionally NULL).
 
 			@returns
 				This function returns a pointer to a #hid_device object on
 				success or NULL on failure.
+				Call hid_error(NULL) to get the failure reason.
+
+			@note The returned object must be freed by calling hid_close(),
+			      when not needed anymore.
 		*/
 		HID_API_EXPORT hid_device * HID_API_CALL hid_open(unsigned short vendor_id, unsigned short product_id, const wchar_t *serial_number);
 
@@ -189,13 +283,17 @@ namespace NAMESPACE {
 			Linux).
 
 			@ingroup API
-		    @param path The path name of the device to open
+			@param path The path name of the device to open
 
 			@returns
 				This function returns a pointer to a #hid_device object on
 				success or NULL on failure.
+				Call hid_error(NULL) to get the failure reason.
+
+			@note The returned object must be freed by calling hid_close(),
+			      when not needed anymore.
 		*/
-		HID_API_EXPORT hid_device * HID_API_CALL hid_open_path(const char *path, int bExclusive /* = false */);
+		HID_API_EXPORT hid_device * HID_API_CALL hid_open_path(const char *path);
 
 		/** @brief Write an Output report to a HID device.
 
@@ -222,6 +320,7 @@ namespace NAMESPACE {
 			@returns
 				This function returns the actual number of bytes written and
 				-1 on error.
+				Call hid_error(dev) to get the failure reason.
 		*/
 		int  HID_API_EXPORT HID_API_CALL hid_write(hid_device *dev, const unsigned char *data, size_t length);
 
@@ -241,7 +340,9 @@ namespace NAMESPACE {
 
 			@returns
 				This function returns the actual number of bytes read and
-				-1 on error. If no packet was available to be read within
+				-1 on error.
+				Call hid_error(dev) to get the failure reason.
+				If no packet was available to be read within
 				the timeout period, this function returns 0.
 		*/
 		int HID_API_EXPORT HID_API_CALL hid_read_timeout(hid_device *dev, unsigned char *data, size_t length, int milliseconds);
@@ -249,7 +350,7 @@ namespace NAMESPACE {
 		/** @brief Read an Input report from a HID device.
 
 			Input reports are returned
-		    to the host through the INTERRUPT IN endpoint. The first byte will
+			to the host through the INTERRUPT IN endpoint. The first byte will
 			contain the Report number if the device uses numbered reports.
 
 			@ingroup API
@@ -261,7 +362,9 @@ namespace NAMESPACE {
 
 			@returns
 				This function returns the actual number of bytes read and
-				-1 on error. If no packet was available to be read and
+				-1 on error.
+				Call hid_error(dev) to get the failure reason.
+				If no packet was available to be read and
 				the handle is in non-blocking mode, this function returns 0.
 		*/
 		int  HID_API_EXPORT HID_API_CALL hid_read(hid_device *dev, unsigned char *data, size_t length);
@@ -283,6 +386,7 @@ namespace NAMESPACE {
 
 			@returns
 				This function returns 0 on success and -1 on error.
+				Call hid_error(dev) to get the failure reason.
 		*/
 		int  HID_API_EXPORT HID_API_CALL hid_set_nonblocking(hid_device *dev, int nonblock);
 
@@ -311,6 +415,7 @@ namespace NAMESPACE {
 			@returns
 				This function returns the actual number of bytes written and
 				-1 on error.
+				Call hid_error(dev) to get the failure reason.
 		*/
 		int HID_API_EXPORT HID_API_CALL hid_send_feature_report(hid_device *dev, const unsigned char *data, size_t length);
 
@@ -336,9 +441,38 @@ namespace NAMESPACE {
 				This function returns the number of bytes read plus
 				one for the report ID (which is still in the first
 				byte), or -1 on error.
+				Call hid_error(dev) to get the failure reason.
 		*/
 		int HID_API_EXPORT HID_API_CALL hid_get_feature_report(hid_device *dev, unsigned char *data, size_t length);
 
+		/** @brief Get a input report from a HID device.
+
+			Since version 0.10.0, @ref HID_API_VERSION >= HID_API_MAKE_VERSION(0, 10, 0)
+
+			Set the first byte of @p data[] to the Report ID of the
+			report to be read. Make sure to allow space for this
+			extra byte in @p data[]. Upon return, the first byte will
+			still contain the Report ID, and the report data will
+			start in data[1].
+
+			@ingroup API
+			@param dev A device handle returned from hid_open().
+			@param data A buffer to put the read data into, including
+				the Report ID. Set the first byte of @p data[] to the
+				Report ID of the report to be read, or set it to zero
+				if your device does not use numbered reports.
+			@param length The number of bytes to read, including an
+				extra byte for the report ID. The buffer can be longer
+				than the actual report.
+
+			@returns
+				This function returns the number of bytes read plus
+				one for the report ID (which is still in the first
+				byte), or -1 on error.
+				Call hid_error(dev) to get the failure reason.
+		*/
+		int HID_API_EXPORT HID_API_CALL hid_get_input_report(hid_device *dev, unsigned char *data, size_t length);
+
 		/** @brief Close a HID device.
 
 			@ingroup API
@@ -355,6 +489,7 @@ namespace NAMESPACE {
 
 			@returns
 				This function returns 0 on success and -1 on error.
+				Call hid_error(dev) to get the failure reason.
 		*/
 		int HID_API_EXPORT_CALL hid_get_manufacturer_string(hid_device *dev, wchar_t *string, size_t maxlen);
 
@@ -367,6 +502,7 @@ namespace NAMESPACE {
 
 			@returns
 				This function returns 0 on success and -1 on error.
+				Call hid_error(dev) to get the failure reason.
 		*/
 		int HID_API_EXPORT_CALL hid_get_product_string(hid_device *dev, wchar_t *string, size_t maxlen);
 
@@ -379,9 +515,27 @@ namespace NAMESPACE {
 
 			@returns
 				This function returns 0 on success and -1 on error.
+				Call hid_error(dev) to get the failure reason.
 		*/
 		int HID_API_EXPORT_CALL hid_get_serial_number_string(hid_device *dev, wchar_t *string, size_t maxlen);
 
+		/** @brief Get The struct #hid_device_info from a HID device.
+
+			Since version 0.13.0, @ref HID_API_VERSION >= HID_API_MAKE_VERSION(0, 13, 0)
+
+			@ingroup API
+			@param dev A device handle returned from hid_open().
+
+			@returns
+				This function returns a pointer to the struct #hid_device_info
+				for this hid_device, or NULL in the case of failure.
+				Call hid_error(dev) to get the failure reason.
+				This struct is valid until the device is closed with hid_close().
+
+			@note The returned object is owned by the @p dev, and SHOULD NOT be freed by the user.
+		*/
+		struct hid_device_info HID_API_EXPORT * HID_API_CALL hid_get_device_info(hid_device *dev);
+
 		/** @brief Get a string from a HID device, based on its string index.
 
 			@ingroup API
@@ -392,30 +546,79 @@ namespace NAMESPACE {
 
 			@returns
 				This function returns 0 on success and -1 on error.
+				Call hid_error(dev) to get the failure reason.
 		*/
 		int HID_API_EXPORT_CALL hid_get_indexed_string(hid_device *dev, int string_index, wchar_t *string, size_t maxlen);
 
-		/** @brief Get a string describing the last error which occurred.
+		/** @brief Get a report descriptor from a HID device.
+
+			Since version 0.14.0, @ref HID_API_VERSION >= HID_API_MAKE_VERSION(0, 14, 0)
+
+			User has to provide a preallocated buffer where descriptor will be copied to.
+			The recommended size for preallocated buffer is @ref HID_API_MAX_REPORT_DESCRIPTOR_SIZE bytes.
 
 			@ingroup API
 			@param dev A device handle returned from hid_open().
+			@param buf The buffer to copy descriptor into.
+			@param buf_size The size of the buffer in bytes.
 
 			@returns
-				This function returns a string containing the last error
-				which occurred or NULL if none has occurred.
+				This function returns non-negative number of bytes actually copied, or -1 on error.
+		*/
+		int HID_API_EXPORT_CALL hid_get_report_descriptor(hid_device *dev, unsigned char *buf, size_t buf_size);
+
+		/** @brief Get a string describing the last error which occurred.
+
+			This function is intended for logging/debugging purposes.
+
+			This function guarantees to never return NULL.
+			If there was no error in the last function call -
+			the returned string clearly indicates that.
+
+			Any HIDAPI function that can explicitly indicate an execution failure
+			(e.g. by an error code, or by returning NULL) - may set the error string,
+			to be returned by this function.
+
+			Strings returned from hid_error() must not be freed by the user,
+			i.e. owned by HIDAPI library.
+			Device-specific error string may remain allocated at most until hid_close() is called.
+			Global error string may remain allocated at most until hid_exit() is called.
+
+			@ingroup API
+			@param dev A device handle returned from hid_open(),
+			  or NULL to get the last non-device-specific error
+			  (e.g. for errors in hid_open() or hid_enumerate()).
+
+			@returns
+				A string describing the last error (if any).
 		*/
 		HID_API_EXPORT const wchar_t* HID_API_CALL hid_error(hid_device *dev);
 
-#if defined(__IOS__) || defined(__TVOS__)
-		HID_API_EXPORT void HID_API_CALL hid_ble_scan(int active);
-#endif
+		/** @brief Get a runtime version of the library.
 
-#if defined(__cplusplus) && !defined(NAMESPACE)
-}
-#endif
-#ifdef NAMESPACE
+			This function is thread-safe.
+
+			@ingroup API
+
+			@returns
+				Pointer to statically allocated struct, that contains version.
+		*/
+		HID_API_EXPORT const  struct hid_api_version* HID_API_CALL hid_version(void);
+
+
+		/** @brief Get a runtime version string of the library.
+
+			This function is thread-safe.
+
+			@ingroup API
+
+			@returns
+				Pointer to statically allocated string, that contains version string.
+		*/
+		HID_API_EXPORT const char* HID_API_CALL hid_version_str(void);
+
+#ifdef __cplusplus
 }
 #endif
 
 #endif
-

+ 17 - 0
src/hidapi/hidtest/.gitignore

@@ -0,0 +1,17 @@
+Debug
+Release
+*.exp
+*.ilk
+*.lib
+*.suo
+*.vcproj.*
+*.ncb
+*.suo
+*.dll
+*.pdb
+*.o
+.deps/
+.libs/
+hidtest-hidraw
+hidtest-libusb
+hidtest

+ 40 - 0
src/hidapi/hidtest/CMakeLists.txt

@@ -0,0 +1,40 @@
+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}"
+)

+ 11 - 3
src/hidapi/hidtest/Makefile.am

@@ -4,17 +4,25 @@ AM_CPPFLAGS = -I$(top_srcdir)/hidapi/
 if OS_LINUX
 noinst_PROGRAMS = hidtest-libusb hidtest-hidraw
 
-hidtest_hidraw_SOURCES = hidtest.cpp
+hidtest_hidraw_SOURCES = test.c
 hidtest_hidraw_LDADD = $(top_builddir)/linux/libhidapi-hidraw.la
 
-hidtest_libusb_SOURCES = hidtest.cpp
+hidtest_libusb_SOURCES = test.c
 hidtest_libusb_LDADD = $(top_builddir)/libusb/libhidapi-libusb.la
 else
 
 # Other OS's
 noinst_PROGRAMS = hidtest
 
-hidtest_SOURCES = hidtest.cpp
+hidtest_SOURCES = test.c
 hidtest_LDADD = $(top_builddir)/$(backend)/libhidapi.la
 
 endif
+
+if OS_DARWIN
+AM_CPPFLAGS += -I$(top_srcdir)/mac/
+endif
+
+if OS_WINDOWS
+AM_CPPFLAGS += -I$(top_srcdir)/windows/
+endif

+ 0 - 194
src/hidapi/hidtest/hidtest.cpp

@@ -1,194 +0,0 @@
-/*******************************************************
- Windows HID simplification
-
- Alan Ott
- Signal 11 Software
-
- 8/22/2009
-
- Copyright 2009
- 
- 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
-
-int main(int argc, char* argv[])
-{
-	int res;
-	unsigned char buf[256];
-	#define MAX_STR 255
-	wchar_t wstr[MAX_STR];
-	hid_device *handle;
-	int i;
-
-#ifdef WIN32
-	UNREFERENCED_PARAMETER(argc);
-	UNREFERENCED_PARAMETER(argv);
-#endif
-
-	struct hid_device_info *devs, *cur_dev;
-	
-	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("\n");
-		cur_dev = cur_dev->next;
-	}
-	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");
- 		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", wstr[0], wstr);
-	printf("\n");
-
-	// 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 shoud 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.\n");
-		printf("%ls", hid_error(handle));
-	}
-	else {
-		// Print out the returned buffer.
-		printf("Feature Report\n   ");
-		for (i = 0; i < res; i++)
-			printf("%02hhx ", 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()\n");
-		printf("Error: %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)\n");
-
-	// 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;
-	while (res == 0) {
-		res = hid_read(handle, buf, sizeof(buf));
-		if (res == 0)
-			printf("waiting...\n");
-		if (res < 0)
-			printf("Unable to read()\n");
-		#ifdef WIN32
-		Sleep(500);
-		#else
-		usleep(500*1000);
-		#endif
-	}
-
-	printf("Data read:\n   ");
-	// Print out the returned buffer.
-	for (i = 0; i < res; i++)
-		printf("%02hhx ", buf[i]);
-	printf("\n");
-
-	hid_close(handle);
-
-	/* Free static HIDAPI objects. */
-	hid_exit();
-
-#ifdef WIN32
-	system("pause");
-#endif
-
-	return 0;
-}

+ 316 - 0
src/hidapi/hidtest/test.c

@@ -0,0 +1,316 @@
+/*******************************************************
+ 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;
+}

+ 0 - 32
src/hidapi/ios/Makefile-manual

@@ -1,32 +0,0 @@
-###########################################
-# Simple Makefile for HIDAPI test program
-#
-# Alan Ott
-# Signal 11 Software
-# 2010-07-03
-###########################################
-
-all: hidtest
-
-CC=gcc
-CXX=g++
-COBJS=hid.o
-CPPOBJS=../hidtest/hidtest.o
-OBJS=$(COBJS) $(CPPOBJS)
-CFLAGS+=-I../hidapi -Wall -g -c 
-LIBS=-framework CoreBluetooth -framework CoreFoundation
-
-
-hidtest: $(OBJS)
-	g++ -Wall -g $^ $(LIBS) -o hidtest
-
-$(COBJS): %.o: %.c
-	$(CC) $(CFLAGS) $< -o $@
-
-$(CPPOBJS): %.o: %.cpp
-	$(CXX) $(CFLAGS) $< -o $@
-
-clean:
-	rm -f *.o hidtest $(CPPOBJS)
-
-.PHONY: clean

+ 0 - 9
src/hidapi/ios/Makefile.am

@@ -1,9 +0,0 @@
-lib_LTLIBRARIES = libhidapi.la
-libhidapi_la_SOURCES = hid.m
-libhidapi_la_LDFLAGS = $(LTLDFLAGS)
-AM_CPPFLAGS = -I$(top_srcdir)/hidapi/
-
-hdrdir = $(includedir)/hidapi
-hdr_HEADERS = $(top_srcdir)/hidapi/hidapi.h
-
-EXTRA_DIST = Makefile-manual

+ 0 - 996
src/hidapi/ios/hid.m

@@ -1,996 +0,0 @@
-/*
-  Simple DirectMedia Layer
-  Copyright (C) 2021 Valve Corporation
-
-  This software is provided 'as-is', without any express or implied
-  warranty.  In no event will the authors be held liable for any damages
-  arising from the use of this software.
-
-  Permission is granted to anyone to use this software for any purpose,
-  including commercial applications, and to alter it and redistribute it
-  freely, subject to the following restrictions:
-
-  1. The origin of this software must not be misrepresented; you must not
-     claim that you wrote the original software. If you use this software
-     in a product, an acknowledgment in the product documentation would be
-     appreciated but is not required.
-  2. Altered source versions must be plainly marked as such, and must not be
-     misrepresented as being the original software.
-  3. This notice may not be removed or altered from any source distribution.
-*/
-#include "SDL_internal.h"
-
-#ifndef SDL_HIDAPI_DISABLED
-
-
-#define hid_init                        PLATFORM_hid_init
-#define hid_exit                        PLATFORM_hid_exit
-#define hid_enumerate                   PLATFORM_hid_enumerate
-#define hid_free_enumeration            PLATFORM_hid_free_enumeration
-#define hid_open                        PLATFORM_hid_open
-#define hid_open_path                   PLATFORM_hid_open_path
-#define hid_write                       PLATFORM_hid_write
-#define hid_read_timeout                PLATFORM_hid_read_timeout
-#define hid_read                        PLATFORM_hid_read
-#define hid_set_nonblocking             PLATFORM_hid_set_nonblocking
-#define hid_send_feature_report         PLATFORM_hid_send_feature_report
-#define hid_get_feature_report          PLATFORM_hid_get_feature_report
-#define hid_close                       PLATFORM_hid_close
-#define hid_get_manufacturer_string     PLATFORM_hid_get_manufacturer_string
-#define hid_get_product_string          PLATFORM_hid_get_product_string
-#define hid_get_serial_number_string    PLATFORM_hid_get_serial_number_string
-#define hid_get_indexed_string          PLATFORM_hid_get_indexed_string
-#define hid_error                       PLATFORM_hid_error
-
-#include <CoreBluetooth/CoreBluetooth.h>
-#include <QuartzCore/QuartzCore.h>
-#import <UIKit/UIKit.h>
-#import <mach/mach_time.h>
-#include <pthread.h>
-#include <sys/time.h>
-#include <unistd.h>
-#include "../hidapi/hidapi.h"
-
-#define VALVE_USB_VID       0x28DE
-#define D0G_BLE2_PID        0x1106
-
-typedef uint32_t uint32;
-typedef uint64_t uint64;
-
-// enables detailed NSLog logging of feature reports
-#define FEATURE_REPORT_LOGGING	0
-
-#define REPORT_SEGMENT_DATA_FLAG	0x80
-#define REPORT_SEGMENT_LAST_FLAG	0x40
-
-#define VALVE_SERVICE		@"100F6C32-1735-4313-B402-38567131E5F3"
-
-// (READ/NOTIFICATIONS)
-#define VALVE_INPUT_CHAR	@"100F6C33-1735-4313-B402-38567131E5F3"
-
-//  (READ/WRITE)
-#define VALVE_REPORT_CHAR	@"100F6C34-1735-4313-B402-38567131E5F3"
-
-// TODO: create CBUUID's in __attribute__((constructor)) rather than doing [CBUUID UUIDWithString:...] everywhere
-
-#pragma pack(push,1)
-
-typedef struct
-{
-	uint8_t		segmentHeader;
-	uint8_t		featureReportMessageID;
-	uint8_t		length;
-	uint8_t		settingIdentifier;
-	union {
-		uint16_t	usPayload;
-		uint32_t	uPayload;
-		uint64_t	ulPayload;
-		uint8_t		ucPayload[15];
-	};
-} bluetoothSegment;
-
-typedef struct {
-	uint8_t		id;
-	union {
-		bluetoothSegment segment;
-		struct {
-			uint8_t		segmentHeader;
-			uint8_t		featureReportMessageID;
-			uint8_t		length;
-			uint8_t		settingIdentifier;
-			union {
-				uint16_t	usPayload;
-				uint32_t	uPayload;
-				uint64_t	ulPayload;
-				uint8_t		ucPayload[15];
-			};
-		};
-	};
-} hidFeatureReport;
-
-#pragma pack(pop)
-
-size_t GetBluetoothSegmentSize(bluetoothSegment *segment)
-{
-    return segment->length + 3;
-}
-
-#define RingBuffer_cbElem   19
-#define RingBuffer_nElem    4096
-
-typedef struct {
-	int _first, _last;
-	uint8_t _data[ ( RingBuffer_nElem * RingBuffer_cbElem ) ];
-	pthread_mutex_t accessLock;
-} RingBuffer;
-
-static void RingBuffer_init( RingBuffer *this )
-{
-    this->_first = -1;
-    this->_last = 0;
-    pthread_mutex_init( &this->accessLock, 0 );
-}
-
-static bool RingBuffer_write( RingBuffer *this, const uint8_t *src )
-{
-    pthread_mutex_lock( &this->accessLock );
-    memcpy( &this->_data[ this->_last ], src, RingBuffer_cbElem );
-    if ( this->_first == -1 )
-    {
-        this->_first = this->_last;
-    }
-    this->_last = ( this->_last + RingBuffer_cbElem ) % (RingBuffer_nElem * RingBuffer_cbElem);
-    if ( this->_last == this->_first )
-    {
-        this->_first = ( this->_first + RingBuffer_cbElem ) % (RingBuffer_nElem * RingBuffer_cbElem);
-        pthread_mutex_unlock( &this->accessLock );
-        return false;
-    }
-    pthread_mutex_unlock( &this->accessLock );
-    return true;
-}
-
-static bool RingBuffer_read( RingBuffer *this, uint8_t *dst )
-{
-    pthread_mutex_lock( &this->accessLock );
-    if ( this->_first == -1 )
-    {
-        pthread_mutex_unlock( &this->accessLock );
-        return false;
-    }
-    memcpy( dst, &this->_data[ this->_first ], RingBuffer_cbElem );
-    this->_first = ( this->_first + RingBuffer_cbElem ) % (RingBuffer_nElem * RingBuffer_cbElem);
-    if ( this->_first == this->_last )
-    {
-        this->_first = -1;
-    }
-    pthread_mutex_unlock( &this->accessLock );
-    return true;
-}
-
-
-#pragma mark HIDBLEDevice Definition
-
-typedef enum
-{
-	BLEDeviceWaitState_None,
-	BLEDeviceWaitState_Waiting,
-	BLEDeviceWaitState_Complete,
-	BLEDeviceWaitState_Error
-} BLEDeviceWaitState;
-
-@interface HIDBLEDevice : NSObject <CBPeripheralDelegate>
-{
-	RingBuffer _inputReports;
-	uint8_t	_featureReport[20];
-	BLEDeviceWaitState	_waitStateForReadFeatureReport;
-	BLEDeviceWaitState	_waitStateForWriteFeatureReport;
-}
-
-@property (nonatomic, readwrite) bool connected;
-@property (nonatomic, readwrite) bool ready;
-
-@property (nonatomic, strong) CBPeripheral     *bleSteamController;
-@property (nonatomic, strong) CBCharacteristic *bleCharacteristicInput;
-@property (nonatomic, strong) CBCharacteristic *bleCharacteristicReport;
-
-- (id)initWithPeripheral:(CBPeripheral *)peripheral;
-
-@end
-
-
-@interface HIDBLEManager : NSObject <CBCentralManagerDelegate>
-
-@property (nonatomic) int nPendingScans;
-@property (nonatomic) int nPendingPairs;
-@property (nonatomic, strong) CBCentralManager *centralManager;
-@property (nonatomic, strong) NSMapTable<CBPeripheral *, HIDBLEDevice *> *deviceMap;
-@property (nonatomic, retain) dispatch_queue_t bleSerialQueue;
-
-+ (instancetype)sharedInstance;
-- (void)startScan:(int)duration;
-- (void)stopScan;
-- (int)updateConnectedSteamControllers:(BOOL) bForce;
-- (void)appWillResignActiveNotification:(NSNotification *)note;
-- (void)appDidBecomeActiveNotification:(NSNotification *)note;
-
-@end
-
-
-// singleton class - access using HIDBLEManager.sharedInstance
-@implementation HIDBLEManager
-
-+ (instancetype)sharedInstance
-{
-	static HIDBLEManager *sharedInstance = nil;
-	static dispatch_once_t onceToken;
-	dispatch_once(&onceToken, ^{
-		sharedInstance = [HIDBLEManager new];
-		sharedInstance.nPendingScans = 0;
-		sharedInstance.nPendingPairs = 0;
-
-        // Bluetooth is currently only used for Steam Controllers, so check that hint
-        // before initializing Bluetooth, which will prompt the user for permission.
-		if ( SDL_GetHintBoolean( SDL_HINT_JOYSTICK_HIDAPI_STEAM, SDL_FALSE ) )
-		{
-			[[NSNotificationCenter defaultCenter] addObserver:sharedInstance selector:@selector(appWillResignActiveNotification:) name: UIApplicationWillResignActiveNotification object:nil];
-			[[NSNotificationCenter defaultCenter] addObserver:sharedInstance selector:@selector(appDidBecomeActiveNotification:) name:UIApplicationDidBecomeActiveNotification object:nil];
-
-			// receive reports on a high-priority serial-queue. optionally put writes on the serial queue to avoid logical
-			// race conditions talking to the controller from multiple threads, although BLE fragmentation/assembly means
-			// that we can still screw this up.
-			// most importantly we need to consume reports at a high priority to avoid the OS thinking we aren't really
-			// listening to the BLE device, as iOS on slower devices may stop delivery of packets to the app WITHOUT ACTUALLY
-			// DISCONNECTING FROM THE DEVICE if we don't react quickly enough to their delivery.
-			// see also the error-handling states in the peripheral delegate to re-open the device if it gets closed
-			sharedInstance.bleSerialQueue = dispatch_queue_create( "com.valvesoftware.steamcontroller.ble", DISPATCH_QUEUE_SERIAL );
-			dispatch_set_target_queue( sharedInstance.bleSerialQueue, dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_HIGH, 0 ) );
-
-			// creating a CBCentralManager will always trigger a future centralManagerDidUpdateState:
-			// where any scanning gets started or connecting to existing peripherals happens, it's never already in a
-			// powered-on state for a newly launched application.
-			sharedInstance.centralManager = [[CBCentralManager alloc] initWithDelegate:sharedInstance queue:sharedInstance.bleSerialQueue];
-		}
-		sharedInstance.deviceMap = [[NSMapTable alloc] initWithKeyOptions:NSMapTableWeakMemory valueOptions:NSMapTableStrongMemory capacity:4];
-	});
-	return sharedInstance;
-}
-
-// called for NSNotification UIApplicationWillResignActiveNotification
-- (void)appWillResignActiveNotification:(NSNotification *)note
-{
-	// we'll get resign-active notification if pairing is happening.
-	if ( self.nPendingPairs > 0 )
-		return;
-
-	for ( CBPeripheral *peripheral in self.deviceMap )
-	{
-		HIDBLEDevice *steamController = [self.deviceMap objectForKey:peripheral];
-		if ( steamController )
-		{
-			steamController.connected = NO;
-			steamController.ready = NO;
-			[self.centralManager cancelPeripheralConnection:peripheral];
-		}
-	}
-	[self.deviceMap removeAllObjects];
-}
-
-// called for NSNotification UIApplicationDidBecomeActiveNotification
-//  whenever the application comes back from being inactive, trigger a 20s pairing scan and reconnect
-//  any devices that may have paired while we were inactive.
-- (void)appDidBecomeActiveNotification:(NSNotification *)note
-{
-	[self updateConnectedSteamControllers:true];
-	[self startScan:20];
-}
-
-- (int)updateConnectedSteamControllers:(BOOL) bForce
-{
-	static uint64_t s_unLastUpdateTick = 0;
-	static mach_timebase_info_data_t s_timebase_info;
-
-	if ( self.centralManager == nil )
-    {
-		return 0;
-    }
-
-	if (s_timebase_info.denom == 0)
-	{
-		mach_timebase_info( &s_timebase_info );
-	}
-
-	uint64_t ticksNow = mach_approximate_time();
-	if ( !bForce && ( ( (ticksNow - s_unLastUpdateTick) * s_timebase_info.numer ) / s_timebase_info.denom ) < (5ull * NSEC_PER_SEC) )
-		return (int)self.deviceMap.count;
-
-	// we can see previously connected BLE peripherals but can't connect until the CBCentralManager
-	// is fully powered up - only do work when we are in that state
-	if ( self.centralManager.state != CBManagerStatePoweredOn )
-		return (int)self.deviceMap.count;
-
-	// only update our last-check-time if we actually did work, otherwise there can be a long delay during initial power-up
-	s_unLastUpdateTick = mach_approximate_time();
-
-	// if a pair is in-flight, the central manager may still give it back via retrieveConnected... and
-	// cause the SDL layer to attempt to initialize it while some of its endpoints haven't yet been established
-	if ( self.nPendingPairs > 0 )
-		return (int)self.deviceMap.count;
-
-	NSArray<CBPeripheral *> *peripherals = [self.centralManager retrieveConnectedPeripheralsWithServices: @[ [CBUUID UUIDWithString:@"180A"]]];
-	for ( CBPeripheral *peripheral in peripherals )
-	{
-		// we already know this peripheral
-		if ( [self.deviceMap objectForKey: peripheral] != nil )
-			continue;
-
-		NSLog( @"connected peripheral: %@", peripheral );
-		if ( [peripheral.name isEqualToString:@"SteamController"] )
-		{
-			self.nPendingPairs += 1;
-			HIDBLEDevice *steamController = [[HIDBLEDevice alloc] initWithPeripheral:peripheral];
-			[self.deviceMap setObject:steamController forKey:peripheral];
-			[self.centralManager connectPeripheral:peripheral options:nil];
-		}
-	}
-
-	return (int)self.deviceMap.count;
-}
-
-// manual API for folks to start & stop scanning
-- (void)startScan:(int)duration
-{
-	if ( self.centralManager == nil )
-	{
-		return;
-	}
-
-	NSLog( @"BLE: requesting scan for %d seconds", duration );
-	@synchronized (self)
-	{
-		if ( _nPendingScans++ == 0 )
-		{
-			[self.centralManager scanForPeripheralsWithServices:nil options:nil];
-		}
-	}
-
-	if ( duration != 0 )
-	{
-		dispatch_after( dispatch_time( DISPATCH_TIME_NOW, (int64_t)(duration * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
-			[self stopScan];
-		});
-	}
-}
-
-- (void)stopScan
-{
-	if ( self.centralManager == nil )
-	{
-		return;
-	}
-
-	NSLog( @"BLE: stopping scan" );
-	@synchronized (self)
-	{
-		if ( --_nPendingScans <= 0 )
-		{
-			_nPendingScans = 0;
-			[self.centralManager stopScan];
-		}
-	}
-}
-
-
-#pragma mark CBCentralManagerDelegate Implementation
-
-// called whenever the BLE hardware state changes.
-- (void)centralManagerDidUpdateState:(CBCentralManager *)central
-{
-	switch ( central.state )
-	{
-		case CBCentralManagerStatePoweredOn:
-		{
-			NSLog( @"CoreBluetooth BLE hardware is powered on and ready" );
-
-			// at startup, if we have no already attached peripherals, do a 20s scan for new unpaired devices,
-			// otherwise callers should occaisionally do additional scans. we don't want to continuously be
-			// scanning because it drains battery, causes other nearby people to have a hard time pairing their
-			// Steam Controllers, and may also trigger firmware weirdness when a device attempts to start
-			// the pairing sequence multiple times concurrently
-			if ( [self updateConnectedSteamControllers:false] == 0 )
-			{
-				// TODO: we could limit our scan to only peripherals supporting the SteamController service, but
-				//  that service doesn't currently fit in the base advertising packet, we'd need to put it into an
-				//  extended scan packet. Useful optimization downstream, but not currently necessary
-				//	NSArray *services = @[[CBUUID UUIDWithString:VALVE_SERVICE]];
-				[self startScan:20];
-			}
-			break;
-		}
-
-		case CBCentralManagerStatePoweredOff:
-			NSLog( @"CoreBluetooth BLE hardware is powered off" );
-			break;
-
-		case CBCentralManagerStateUnauthorized:
-			NSLog( @"CoreBluetooth BLE state is unauthorized" );
-			break;
-
-		case CBCentralManagerStateUnknown:
-			NSLog( @"CoreBluetooth BLE state is unknown" );
-			break;
-
-		case CBCentralManagerStateUnsupported:
-			NSLog( @"CoreBluetooth BLE hardware is unsupported on this platform" );
-			break;
-
-		case CBCentralManagerStateResetting:
-			NSLog( @"CoreBluetooth BLE manager is resetting" );
-			break;
-	}
-}
-
-- (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral
-{
-	HIDBLEDevice *steamController = [_deviceMap objectForKey:peripheral];
-	steamController.connected = YES;
-	self.nPendingPairs -= 1;
-}
-
-- (void)centralManager:(CBCentralManager *)central didFailToConnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error
-{
-	NSLog( @"Failed to connect: %@", error );
-	[_deviceMap removeObjectForKey:peripheral];
-	self.nPendingPairs -= 1;
-}
-
-- (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI
-{
-	NSString *localName = [advertisementData objectForKey:CBAdvertisementDataLocalNameKey];
-	NSString *log = [NSString stringWithFormat:@"Found '%@'", localName];
-
-	if ( [localName isEqualToString:@"SteamController"] )
-	{
-		NSLog( @"%@ : %@ - %@", log, peripheral, advertisementData );
-		self.nPendingPairs += 1;
-		HIDBLEDevice *steamController = [[HIDBLEDevice alloc] initWithPeripheral:peripheral];
-		[self.deviceMap setObject:steamController forKey:peripheral];
-		[self.centralManager connectPeripheral:peripheral options:nil];
-	}
-}
-
-- (void)centralManager:(CBCentralManager *)central didDisconnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error
-{
-	HIDBLEDevice *steamController = [self.deviceMap objectForKey:peripheral];
-	if ( steamController )
-	{
-		steamController.connected = NO;
-		steamController.ready = NO;
-		[self.deviceMap removeObjectForKey:peripheral];
-	}
-}
-
-@end
-
-
-// Core Bluetooth devices calling back on event boundaries of their run-loops. so annoying.
-static void process_pending_events()
-{
-	CFRunLoopRunResult res;
-	do
-	{
-		res = CFRunLoopRunInMode( kCFRunLoopDefaultMode, 0.001, FALSE );
-	}
-	while( res != kCFRunLoopRunFinished && res != kCFRunLoopRunTimedOut );
-}
-
-@implementation HIDBLEDevice
-
-- (id)init
-{
-	if ( self = [super init] )
-	{
-        RingBuffer_init( &_inputReports );
-		self.bleSteamController = nil;
-		self.bleCharacteristicInput = nil;
-		self.bleCharacteristicReport = nil;
-		_connected = NO;
-		_ready = NO;
-	}
-	return self;
-}
-
-- (id)initWithPeripheral:(CBPeripheral *)peripheral
-{
-	if ( self = [super init] )
-	{
-        RingBuffer_init( &_inputReports );
-		_connected = NO;
-		_ready = NO;
-		self.bleSteamController = peripheral;
-		if ( peripheral )
-		{
-			peripheral.delegate = self;
-		}
-		self.bleCharacteristicInput = nil;
-		self.bleCharacteristicReport = nil;
-	}
-	return self;
-}
-
-- (void)setConnected:(bool)connected
-{
-	_connected = connected;
-	if ( _connected )
-	{
-		[_bleSteamController discoverServices:nil];
-	}
-	else
-	{
-		NSLog( @"Disconnected" );
-	}
-}
-
-- (size_t)read_input_report:(uint8_t *)dst
-{
-	if ( RingBuffer_read( &_inputReports, dst+1 ) )
-	{
-		*dst = 0x03;
-		return 20;
-	}
-	return 0;
-}
-
-- (int)send_report:(const uint8_t *)data length:(size_t)length
-{
-	[_bleSteamController writeValue:[NSData dataWithBytes:data length:length] forCharacteristic:_bleCharacteristicReport type:CBCharacteristicWriteWithResponse];
-	return (int)length;
-}
-
-- (int)send_feature_report:(hidFeatureReport *)report
-{
-#if FEATURE_REPORT_LOGGING
-	uint8_t *reportBytes = (uint8_t *)report;
-
-	NSLog( @"HIDBLE:send_feature_report (%02zu/19) [%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x]", GetBluetoothSegmentSize( report->segment ),
-		  reportBytes[1], reportBytes[2], reportBytes[3], reportBytes[4], reportBytes[5], reportBytes[6],
-		  reportBytes[7], reportBytes[8], reportBytes[9], reportBytes[10], reportBytes[11], reportBytes[12],
-		  reportBytes[13], reportBytes[14], reportBytes[15], reportBytes[16], reportBytes[17], reportBytes[18],
-		  reportBytes[19] );
-#endif
-
-	int sendSize = (int)GetBluetoothSegmentSize( &report->segment );
-	if ( sendSize > 20 )
-		sendSize = 20;
-
-#if 1
-	// fire-and-forget - we are going to not wait for the response here because all Steam Controller BLE send_feature_report's are ignored,
-	//  except errors.
-	[_bleSteamController writeValue:[NSData dataWithBytes:&report->segment length:sendSize] forCharacteristic:_bleCharacteristicReport type:CBCharacteristicWriteWithResponse];
-
-	// pretend we received a result anybody cares about
-	return 19;
-
-#else
-	// this is technically the correct send_feature_report logic if you want to make sure it gets through and is
-	// acknowledged or errors out
-	_waitStateForWriteFeatureReport = BLEDeviceWaitState_Waiting;
-	[_bleSteamController writeValue:[NSData dataWithBytes:&report->segment length:sendSize
-									 ] forCharacteristic:_bleCharacteristicReport type:CBCharacteristicWriteWithResponse];
-
-	while ( _waitStateForWriteFeatureReport == BLEDeviceWaitState_Waiting )
-	{
-		process_pending_events();
-	}
-
-	if ( _waitStateForWriteFeatureReport == BLEDeviceWaitState_Error )
-	{
-		_waitStateForWriteFeatureReport = BLEDeviceWaitState_None;
-		return -1;
-	}
-
-	_waitStateForWriteFeatureReport = BLEDeviceWaitState_None;
-	return 19;
-#endif
-}
-
-- (int)get_feature_report:(uint8_t)feature into:(uint8_t *)buffer
-{
-	_waitStateForReadFeatureReport = BLEDeviceWaitState_Waiting;
-	[_bleSteamController readValueForCharacteristic:_bleCharacteristicReport];
-
-	while ( _waitStateForReadFeatureReport == BLEDeviceWaitState_Waiting )
-		process_pending_events();
-
-	if ( _waitStateForReadFeatureReport == BLEDeviceWaitState_Error )
-	{
-		_waitStateForReadFeatureReport = BLEDeviceWaitState_None;
-		return -1;
-	}
-
-	memcpy( buffer, _featureReport, sizeof(_featureReport) );
-
-	_waitStateForReadFeatureReport = BLEDeviceWaitState_None;
-
-#if FEATURE_REPORT_LOGGING
-	NSLog( @"HIDBLE:get_feature_report (19) [%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x]",
-		  buffer[1], buffer[2], buffer[3], buffer[4], buffer[5], buffer[6],
-		  buffer[7], buffer[8], buffer[9], buffer[10], buffer[11], buffer[12],
-		  buffer[13], buffer[14], buffer[15], buffer[16], buffer[17], buffer[18],
-		  buffer[19] );
-#endif
-
-	return 19;
-}
-
-#pragma mark CBPeripheralDelegate Implementation
-
-- (void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error
-{
-	for (CBService *service in peripheral.services)
-	{
-		NSLog( @"Found Service: %@", service );
-		if ( [service.UUID isEqual:[CBUUID UUIDWithString:VALVE_SERVICE]] )
-		{
-			[peripheral discoverCharacteristics:nil forService:service];
-		}
-	}
-}
-
-- (void)peripheral:(CBPeripheral *)peripheral didDiscoverDescriptorsForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error
-{
-	// nothing yet needed here, enable for logging
-	if ( /* DISABLES CODE */ (0) )
-	{
-		for ( CBDescriptor *descriptor in characteristic.descriptors )
-		{
-			NSLog( @" - Descriptor '%@'", descriptor );
-		}
-	}
-}
-
-- (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error
-{
-	if ([service.UUID isEqual:[CBUUID UUIDWithString:VALVE_SERVICE]])
-	{
-		for (CBCharacteristic *aChar in service.characteristics)
-		{
-			NSLog( @"Found Characteristic %@", aChar );
-
-			if ( [aChar.UUID isEqual:[CBUUID UUIDWithString:VALVE_INPUT_CHAR]] )
-			{
-				self.bleCharacteristicInput = aChar;
-			}
-			else if ( [aChar.UUID isEqual:[CBUUID UUIDWithString:VALVE_REPORT_CHAR]] )
-			{
-				self.bleCharacteristicReport = aChar;
-				[self.bleSteamController discoverDescriptorsForCharacteristic: aChar];
-			}
-		}
-	}
-}
-
-- (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error
-{
-	static uint64_t s_ticksLastOverflowReport = 0;
-
-	// receiving an input report is the final indicator that the user accepted a pairing
-	// request and that we successfully established notification. CoreBluetooth has no
-	// notification of the pairing acknowledgement, which is a bad oversight.
-	if ( self.ready == NO )
-	{
-		self.ready = YES;
-		HIDBLEManager.sharedInstance.nPendingPairs -= 1;
-	}
-
-	if ( [characteristic.UUID isEqual:_bleCharacteristicInput.UUID] )
-	{
-		NSData *data = [characteristic value];
-		if ( data.length != 19 )
-		{
-			NSLog( @"HIDBLE: incoming data is %lu bytes should be exactly 19", (unsigned long)data.length );
-		}
-		if ( !RingBuffer_write( &_inputReports, (const uint8_t *)data.bytes ) )
-		{
-			uint64_t ticksNow = mach_approximate_time();
-			if ( ticksNow - s_ticksLastOverflowReport > (5ull * NSEC_PER_SEC / 10) )
-			{
-				NSLog( @"HIDBLE: input report buffer overflow" );
-				s_ticksLastOverflowReport = ticksNow;
-			}
-		}
-	}
-	else if ( [characteristic.UUID isEqual:_bleCharacteristicReport.UUID] )
-	{
-		memset( _featureReport, 0, sizeof(_featureReport) );
-
-		if ( error != nil )
-		{
-			NSLog( @"HIDBLE: get_feature_report error: %@", error );
-			_waitStateForReadFeatureReport = BLEDeviceWaitState_Error;
-		}
-		else
-		{
-			NSData *data = [characteristic value];
-			if ( data.length != 20 )
-			{
-				NSLog( @"HIDBLE: incoming data is %lu bytes should be exactly 20", (unsigned long)data.length );
-			}
-			memcpy( _featureReport, data.bytes, MIN( data.length, sizeof(_featureReport) ) );
-			_waitStateForReadFeatureReport = BLEDeviceWaitState_Complete;
-		}
-	}
-}
-
-- (void)peripheral:(CBPeripheral *)peripheral didWriteValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error
-{
-	if ( [characteristic.UUID isEqual:[CBUUID UUIDWithString:VALVE_REPORT_CHAR]] )
-	{
-		if ( error != nil )
-		{
-			NSLog( @"HIDBLE: write_feature_report error: %@", error );
-			_waitStateForWriteFeatureReport = BLEDeviceWaitState_Error;
-		}
-		else
-		{
-			_waitStateForWriteFeatureReport = BLEDeviceWaitState_Complete;
-		}
-	}
-}
-
-- (void)peripheral:(CBPeripheral *)peripheral didUpdateNotificationStateForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error
-{
-	NSLog( @"didUpdateNotifcationStateForCharacteristic %@ (%@)", characteristic, error );
-}
-
-@end
-
-
-#pragma mark hid_api implementation
-
-struct hid_device_ {
-	void *device_handle;
-	int blocking;
-	hid_device *next;
-};
-
-int HID_API_EXPORT HID_API_CALL hid_init(void)
-{
-	return ( HIDBLEManager.sharedInstance == nil ) ? -1 : 0;
-}
-
-int HID_API_EXPORT HID_API_CALL hid_exit(void)
-{
-	return 0;
-}
-
-void HID_API_EXPORT HID_API_CALL hid_ble_scan( int bStart )
-{
-	HIDBLEManager *bleManager = HIDBLEManager.sharedInstance;
-	if ( bStart )
-	{
-		[bleManager startScan:0];
-	}
-	else
-	{
-		[bleManager stopScan];
-	}
-}
-
-HID_API_EXPORT hid_device * HID_API_CALL hid_open(unsigned short vendor_id, unsigned short product_id, const wchar_t *serial_number)
-{
-	return NULL;
-}
-
-HID_API_EXPORT hid_device * HID_API_CALL hid_open_path( const char *path, int bExclusive /* = false */ )
-{
-	hid_device *result = NULL;
-	NSString *nssPath = [NSString stringWithUTF8String:path];
-	HIDBLEManager *bleManager = HIDBLEManager.sharedInstance;
-	NSEnumerator<HIDBLEDevice *> *devices = [bleManager.deviceMap objectEnumerator];
-
-	for ( HIDBLEDevice *device in devices )
-	{
-		// we have the device but it hasn't found its service or characteristics until it is connected
-		if ( !device.ready || !device.connected || !device.bleCharacteristicInput )
-			continue;
-
-		if ( [device.bleSteamController.identifier.UUIDString isEqualToString:nssPath] )
-		{
-			result = (hid_device *)malloc( sizeof( hid_device ) );
-			memset( result, 0, sizeof( hid_device ) );
-			result->device_handle = (void*)CFBridgingRetain( device );
-			result->blocking = NO;
-			// enable reporting input events on the characteristic
-			[device.bleSteamController setNotifyValue:YES forCharacteristic:device.bleCharacteristicInput];
-			return result;
-		}
-	}
-	return result;
-}
-
-void  HID_API_EXPORT hid_free_enumeration(struct hid_device_info *devs)
-{
-	/* This function is identical to the Linux version. Platform independent. */
-	struct hid_device_info *d = devs;
-	while (d) {
-		struct hid_device_info *next = d->next;
-		free(d->path);
-		free(d->serial_number);
-		free(d->manufacturer_string);
-		free(d->product_string);
-		free(d);
-		d = next;
-	}
-}
-
-int HID_API_EXPORT hid_set_nonblocking(hid_device *dev, int nonblock)
-{
-	/* All Nonblocking operation is handled by the library. */
-	dev->blocking = !nonblock;
-
-	return 0;
-}
-
-struct hid_device_info  HID_API_EXPORT *hid_enumerate(unsigned short vendor_id, unsigned short product_id)
-{ @autoreleasepool {
-	struct hid_device_info *root = NULL;
-	const char *hint = SDL_GetHint(SDL_HINT_HIDAPI_IGNORE_DEVICES);
-
-	/* See if there are any devices we should skip in enumeration */
-	if (hint) {
-		char vendor_match[16], product_match[16];
-		SDL_snprintf(vendor_match, sizeof(vendor_match), "0x%.4x/0x0000", VALVE_USB_VID);
-		SDL_snprintf(product_match, sizeof(product_match), "0x%.4x/0x%.4x", VALVE_USB_VID, D0G_BLE2_PID);
-		if (SDL_strcasestr(hint, vendor_match) || SDL_strcasestr(hint, product_match)) {
-			return NULL;
-		}
-	}
-
-	if ( ( vendor_id == 0 && product_id == 0 ) ||
-		 ( vendor_id == VALVE_USB_VID && product_id == D0G_BLE2_PID ) )
-	{
-		HIDBLEManager *bleManager = HIDBLEManager.sharedInstance;
-		[bleManager updateConnectedSteamControllers:false];
-		NSEnumerator<HIDBLEDevice *> *devices = [bleManager.deviceMap objectEnumerator];
-		for ( HIDBLEDevice *device in devices )
-		{
-			// there are several brief windows in connecting to an already paired device and
-			// one long window waiting for users to confirm pairing where we don't want
-			// to consider a device ready - if we hand it back to SDL or another
-			// Steam Controller consumer, their additional SC setup work will fail
-			// in unusual/silent ways and we can actually corrupt the BLE stack for
-			// the entire system and kill the appletv remote's Menu button (!)
-			if ( device.bleSteamController.state != CBPeripheralStateConnected ||
-				 device.connected == NO || device.ready == NO )
-			{
-				if ( device.ready == NO && device.bleCharacteristicInput != nil )
-				{
-					// attempt to register for input reports. this call will silently fail
-					// until the pairing finalizes with user acceptance. oh, apple.
-					[device.bleSteamController setNotifyValue:YES forCharacteristic:device.bleCharacteristicInput];
-				}
-				continue;
-			}
-			struct hid_device_info *device_info = (struct hid_device_info *)malloc( sizeof(struct hid_device_info) );
-			memset( device_info, 0, sizeof(struct hid_device_info) );
-			device_info->next = root;
-			root = device_info;
-			device_info->path = strdup( device.bleSteamController.identifier.UUIDString.UTF8String );
-			device_info->vendor_id = VALVE_USB_VID;
-			device_info->product_id = D0G_BLE2_PID;
-			device_info->product_string = wcsdup( L"Steam Controller" );
-			device_info->manufacturer_string = wcsdup( L"Valve Corporation" );
-		}
-	}
-	return root;
-}}
-
-int HID_API_EXPORT_CALL hid_get_manufacturer_string(hid_device *dev, wchar_t *string, size_t maxlen)
-{
-	static wchar_t s_wszManufacturer[] = L"Valve Corporation";
-	wcsncpy( string, s_wszManufacturer, sizeof(s_wszManufacturer)/sizeof(s_wszManufacturer[0]) );
-	return 0;
-}
-
-int HID_API_EXPORT_CALL hid_get_product_string(hid_device *dev, wchar_t *string, size_t maxlen)
-{
-	static wchar_t s_wszProduct[] = L"Steam Controller";
-	wcsncpy( string, s_wszProduct, sizeof(s_wszProduct)/sizeof(s_wszProduct[0]) );
-	return 0;
-}
-
-int HID_API_EXPORT_CALL hid_get_serial_number_string(hid_device *dev, wchar_t *string, size_t maxlen)
-{
-	static wchar_t s_wszSerial[] = L"12345";
-	wcsncpy( string, s_wszSerial, sizeof(s_wszSerial)/sizeof(s_wszSerial[0]) );
-	return 0;
-}
-
-int HID_API_EXPORT_CALL hid_get_indexed_string(hid_device *dev, int string_index, wchar_t *string, size_t maxlen)
-{
-	return -1;
-}
-
-int HID_API_EXPORT hid_write(hid_device *dev, const unsigned char *data, size_t length)
-{
-    HIDBLEDevice *device_handle = (__bridge HIDBLEDevice *)dev->device_handle;
-
-	if ( !device_handle.connected )
-		return -1;
-
-	return [device_handle send_report:data length:length];
-}
-
-void HID_API_EXPORT hid_close(hid_device *dev)
-{
-    HIDBLEDevice *device_handle = CFBridgingRelease( dev->device_handle );
-
-	// disable reporting input events on the characteristic
-	if ( device_handle.connected ) {
-		[device_handle.bleSteamController setNotifyValue:NO forCharacteristic:device_handle.bleCharacteristicInput];
-	}
-
-	free( dev );
-}
-
-int HID_API_EXPORT hid_send_feature_report(hid_device *dev, const unsigned char *data, size_t length)
-{
-    HIDBLEDevice *device_handle = (__bridge HIDBLEDevice *)dev->device_handle;
-
-	if ( !device_handle.connected )
-		return -1;
-
-	return [device_handle send_feature_report:(hidFeatureReport *)(void *)data];
-}
-
-int HID_API_EXPORT hid_get_feature_report(hid_device *dev, unsigned char *data, size_t length)
-{
-    HIDBLEDevice *device_handle = (__bridge HIDBLEDevice *)dev->device_handle;
-
-	if ( !device_handle.connected )
-		return -1;
-
-	size_t written = [device_handle get_feature_report:data[0] into:data];
-
-	return written == length-1 ? (int)length : (int)written;
-}
-
-int HID_API_EXPORT hid_read(hid_device *dev, unsigned char *data, size_t length)
-{
-    HIDBLEDevice *device_handle = (__bridge HIDBLEDevice *)dev->device_handle;
-
-	if ( !device_handle.connected )
-		return -1;
-
-	return hid_read_timeout(dev, data, length, 0);
-}
-
-int HID_API_EXPORT hid_read_timeout(hid_device *dev, unsigned char *data, size_t length, int milliseconds)
-{
-    HIDBLEDevice *device_handle = (__bridge HIDBLEDevice *)dev->device_handle;
-
-	if ( !device_handle.connected )
-		return -1;
-
-	if ( milliseconds != 0 )
-	{
-		NSLog( @"hid_read_timeout with non-zero wait" );
-	}
-	int result = (int)[device_handle read_input_report:data];
-#if FEATURE_REPORT_LOGGING
-	NSLog( @"HIDBLE:hid_read_timeout (%d) [%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x]", result,
-		  data[1], data[2], data[3], data[4], data[5], data[6],
-		  data[7], data[8], data[9], data[10], data[11], data[12],
-		  data[13], data[14], data[15], data[16], data[17], data[18],
-		  data[19] );
-#endif
-	return result;
-}
-
-HID_API_EXPORT const wchar_t* HID_API_CALL hid_error(hid_device *dev)
-{
-	return NULL;
-}
-
-#endif /* !SDL_HIDAPI_DISABLED */

+ 8 - 0
src/hidapi/libusb/.gitignore

@@ -0,0 +1,8 @@
+*.o
+*.so
+*.la
+*.lo
+*.a
+.libs
+.deps
+hidtest-libusb

+ 107 - 0
src/hidapi/libusb/CMakeLists.txt

@@ -0,0 +1,107 @@
+cmake_minimum_required(VERSION 3.6.3 FATAL_ERROR)
+
+list(APPEND HIDAPI_PUBLIC_HEADERS "hidapi_libusb.h")
+
+add_library(hidapi_libusb
+    ${HIDAPI_PUBLIC_HEADERS}
+    hid.c
+)
+target_link_libraries(hidapi_libusb PUBLIC hidapi_include)
+
+if(TARGET usb-1.0)
+    target_link_libraries(hidapi_libusb PRIVATE usb-1.0)
+else()
+    include(FindPkgConfig)
+    pkg_check_modules(libusb REQUIRED IMPORTED_TARGET libusb-1.0>=1.0.9)
+    target_link_libraries(hidapi_libusb PRIVATE PkgConfig::libusb)
+endif()
+
+find_package(Threads REQUIRED)
+target_link_libraries(hidapi_libusb PRIVATE Threads::Threads)
+
+if(HIDAPI_NO_ICONV)
+    target_compile_definitions(hidapi_libusb PRIVATE NO_ICONV)
+else()
+    if(NOT ANDROID)
+        include(CheckCSourceCompiles)
+
+        if(NOT CMAKE_VERSION VERSION_LESS 3.11)
+            message(STATUS "Check for Iconv")
+            find_package(Iconv)
+            if(Iconv_FOUND)
+                if(NOT Iconv_IS_BUILT_IN)
+                    target_link_libraries(hidapi_libusb PRIVATE Iconv::Iconv)
+                    set(CMAKE_REQUIRED_LIBRARIES "Iconv::Iconv")
+                    if(NOT BUILD_SHARED_LIBS)
+                        set(HIDAPI_NEED_EXPORT_ICONV TRUE PARENT_SCOPE)
+                    endif()
+                endif()
+            else()
+                message(STATUS "Iconv Explicitly check '-liconv'")
+                # Sometime the build environment is not setup
+                # in a way CMake can find Iconv on its own by default.
+                # But if we simply link against iconv (-liconv), the build may succeed
+                # due to other compiler/link flags.
+                set(CMAKE_REQUIRED_LIBRARIES "iconv")
+                check_c_source_compiles("
+                    #include <stddef.h>
+                    #include <iconv.h>
+                    int main() {
+                        char *a, *b;
+                        size_t i, j;
+                        iconv_t ic;
+                        ic = iconv_open(\"to\", \"from\");
+                        iconv(ic, &a, &i, &b, &j);
+                        iconv_close(ic);
+                    }
+                    "
+                    Iconv_EXPLICITLY_AT_ENV)
+                if(Iconv_EXPLICITLY_AT_ENV)
+                    message(STATUS "Iconv Explicitly check '-liconv' - Available")
+                    target_link_libraries(hidapi_libusb PRIVATE iconv)
+                else()
+                    message(FATAL_ERROR "Iconv is not found, make sure to provide it in the build environment")
+                endif()
+            endif()
+        else()
+            # otherwise there is 2 options:
+            # 1) iconv is provided by Standard C library and the build will be just fine
+            # 2) The _user_ has to provide additiona compilation options for this project/target
+        endif()
+
+        # check for error: "conflicting types for 'iconv'"
+        check_c_source_compiles("#include<iconv.h>
+            extern size_t iconv (iconv_t cd, const char **inbuf, size_t *inbytesleft, char **outbuf, size_t *outbytesleft);
+            int main() {}"
+        HIDAPI_ICONV_CONST)
+        if(HIDAPI_ICONV_CONST)
+            target_compile_definitions(hidapi_libusb PRIVATE "ICONV_CONST=const")
+        endif()
+    else()
+        # On Android Iconv is disabled on the code level anyway, so no issue;
+    endif()
+endif()
+
+set_target_properties(hidapi_libusb
+    PROPERTIES
+        EXPORT_NAME "libusb"
+        OUTPUT_NAME "hidapi-libusb"
+        VERSION ${PROJECT_VERSION}
+        SOVERSION ${PROJECT_VERSION_MAJOR}
+        PUBLIC_HEADER "${HIDAPI_PUBLIC_HEADERS}"
+)
+
+# compatibility with find_package()
+add_library(hidapi::libusb ALIAS hidapi_libusb)
+# compatibility with raw library link
+add_library(hidapi-libusb ALIAS hidapi_libusb)
+
+if(HIDAPI_INSTALL_TARGETS)
+    install(TARGETS hidapi_libusb EXPORT hidapi
+        LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}"
+        ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}"
+        PUBLIC_HEADER DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/hidapi"
+    )
+endif()
+
+hidapi_configure_pc("${PROJECT_ROOT}/pc/hidapi-libusb.pc.in")

+ 4 - 0
src/hidapi/libusb/Makefile-manual

@@ -10,6 +10,10 @@ ifeq ($(OS), FreeBSD)
 	FILE=Makefile.freebsd
 endif
 
+ifeq ($(OS), Haiku)
+	FILE=Makefile.haiku
+endif
+
 ifeq ($(FILE), )
 all:
 	$(error Your platform ${OS} is not supported by hidapi/libusb at this time.)

+ 8 - 1
src/hidapi/libusb/Makefile.am

@@ -21,7 +21,14 @@ libhidapi_la_LDFLAGS = $(LTLDFLAGS)
 libhidapi_la_LIBADD = $(LIBS_LIBUSB)
 endif
 
+if OS_HAIKU
+lib_LTLIBRARIES = libhidapi.la
+libhidapi_la_SOURCES = hid.c
+libhidapi_la_LDFLAGS = $(LTLDFLAGS)
+libhidapi_la_LIBADD = $(LIBS_LIBUSB)
+endif
+
 hdrdir = $(includedir)/hidapi
-hdr_HEADERS = $(top_srcdir)/hidapi/hidapi.h
+hdr_HEADERS = $(top_srcdir)/hidapi/hidapi.h hidapi_libusb.h
 
 EXTRA_DIST = Makefile-manual

+ 4 - 11
src/hidapi/libusb/Makefile.freebsd

@@ -13,20 +13,16 @@ libs: libhidapi.so
 CC       ?= cc
 CFLAGS   ?= -Wall -g -fPIC
 
-CXX      ?= c++
-CXXFLAGS ?= -Wall -g
-
-COBJS     = hid.o
-CPPOBJS   = ../hidtest/hidtest.o
-OBJS      = $(COBJS) $(CPPOBJS)
-INCLUDES  = -I../hidapi -I/usr/local/include
+COBJS     = hid.o ../hidtest/test.o
+OBJS      = $(COBJS)
+INCLUDES  = -I../hidapi -I. -I/usr/local/include
 LDFLAGS   = -L/usr/local/lib
 LIBS      = -lusb -liconv -pthread
 
 
 # Console Test Program
 hidtest: $(OBJS)
-	$(CXX) $(CXXFLAGS) $(LDFLAGS) $^ -o $@ $(LIBS)
+	$(CC) $(CFLAGS) $(LDFLAGS) $^ -o $@ $(LIBS)
 
 # Shared Libs
 libhidapi.so: $(COBJS)
@@ -36,9 +32,6 @@ libhidapi.so: $(COBJS)
 $(COBJS): %.o: %.c
 	$(CC) $(CFLAGS) -c $(INCLUDES) $< -o $@
 
-$(CPPOBJS): %.o: %.cpp
-	$(CXX) $(CXXFLAGS) -c $(INCLUDES) $< -o $@
-
 
 clean:
 	rm -f $(OBJS) hidtest libhidapi.so ../hidtest/hidtest.o

+ 39 - 0
src/hidapi/libusb/Makefile.haiku

@@ -0,0 +1,39 @@
+###########################################
+# Simple Makefile for HIDAPI test program
+#
+# Alan Ott
+# Signal 11 Software
+# 2010-06-01
+###########################################
+
+all: hidtest libs
+
+libs: libhidapi.so
+
+CC       ?= cc
+CFLAGS   ?= -Wall -g -fPIC
+
+COBJS     = hid.o ../hidtest/test.o
+OBJS      = $(COBJS)
+INCLUDES  = -I../hidapi -I. -I/usr/local/include
+LDFLAGS   = -L/usr/local/lib
+LIBS      = -lusb -liconv -pthread
+
+
+# Console Test Program
+hidtest: $(OBJS)
+    $(CC) $(CFLAGS) $(LDFLAGS) $^ -o $@ $(LIBS)
+
+# Shared Libs
+libhidapi.so: $(COBJS)
+    $(CC) $(LDFLAGS) -shared -Wl,-soname,[email protected] $^ -o $@ $(LIBS)
+
+# Objects
+$(COBJS): %.o: %.c
+    $(CC) $(CFLAGS) -c $(INCLUDES) $< -o $@
+
+
+clean:
+    rm -f $(OBJS) hidtest libhidapi.so ../hidtest/hidtest.o
+
+.PHONY: clean libs

+ 5 - 12
src/hidapi/libusb/Makefile.linux

@@ -13,23 +13,19 @@ libs: libhidapi-libusb.so
 CC       ?= gcc
 CFLAGS   ?= -Wall -g -fpic
 
-CXX      ?= g++
-CXXFLAGS ?= -Wall -g -fpic
-
 LDFLAGS  ?= -Wall -g
 
 COBJS_LIBUSB = hid.o
-COBJS = $(COBJS_LIBUSB)
-CPPOBJS   = ../hidtest/hidtest.o
-OBJS      = $(COBJS) $(CPPOBJS)
+COBJS = $(COBJS_LIBUSB) ../hidtest/test.o
+OBJS      = $(COBJS)
 LIBS_USB  = `pkg-config libusb-1.0 --libs` -lrt -lpthread
 LIBS      = $(LIBS_USB)
-INCLUDES ?= -I../hidapi `pkg-config libusb-1.0 --cflags`
+INCLUDES ?= -I../hidapi -I. `pkg-config libusb-1.0 --cflags`
 
 
 # Console Test Program
-hidtest-libusb: $(COBJS_LIBUSB) $(CPPOBJS)
-	$(CXX) $(LDFLAGS) $^ $(LIBS_USB) -o $@
+hidtest-libusb: $(COBJS)
+	$(CC) $(LDFLAGS) $^ $(LIBS_USB) -o $@
 
 # Shared Libs
 libhidapi-libusb.so: $(COBJS_LIBUSB)
@@ -39,9 +35,6 @@ libhidapi-libusb.so: $(COBJS_LIBUSB)
 $(COBJS): %.o: %.c
 	$(CC) $(CFLAGS) -c $(INCLUDES) $< -o $@
 
-$(CPPOBJS): %.o: %.cpp
-	$(CXX) $(CXXFLAGS) -c $(INCLUDES) $< -o $@
-
 
 clean:
 	rm -f $(OBJS) hidtest-libusb libhidapi-libusb.so ../hidtest/hidtest.o

File diff suppressed because it is too large
+ 357 - 451
src/hidapi/libusb/hid.c


+ 56 - 0
src/hidapi/libusb/hidapi_libusb.h

@@ -0,0 +1,56 @@
+/*******************************************************
+ HIDAPI - Multi-Platform library for
+ communication with HID devices.
+
+ libusb/hidapi Team
+
+ Copyright 2021, All Rights Reserved.
+
+ At the discretion of the user of this library,
+ this software may be licensed under the terms of the
+ GNU General Public License v3, a BSD-Style license, or the
+ original HIDAPI license as outlined in the LICENSE.txt,
+ LICENSE-gpl3.txt, LICENSE-bsd.txt, and LICENSE-orig.txt
+ files located at the root of the source distribution.
+ These files may also be found in the public source
+ code repository located at:
+        https://github.com/libusb/hidapi .
+********************************************************/
+
+/** @file
+ * @defgroup API hidapi API
+
+ * Since version 0.11.0, @ref HID_API_VERSION >= HID_API_MAKE_VERSION(0, 11, 0).
+ */
+
+#ifndef HIDAPI_LIBUSB_H__
+#define HIDAPI_LIBUSB_H__
+
+#include <stdint.h>
+
+#include "hidapi.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+		/** @brief Open a HID device using libusb_wrap_sys_device.
+			See https://libusb.sourceforge.io/api-1.0/group__libusb__dev.html#ga98f783e115ceff4eaf88a60e6439563c,
+			for details on libusb_wrap_sys_device.
+
+			@ingroup API
+			@param sys_dev Platform-specific file descriptor that can be recognised by libusb.
+			@param interface_num USB interface number of the device to be used as HID interface.
+			Pass -1 to select first HID interface of the device.
+
+			@returns
+				This function returns a pointer to a #hid_device object on
+				success or NULL on failure.
+		*/
+		HID_API_EXPORT hid_device * HID_API_CALL hid_libusb_wrap_sys_device(intptr_t sys_dev, int interface_num);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif

+ 0 - 3
src/hidapi/libusb/hidusb.cpp

@@ -1,3 +0,0 @@
-
-#define NAMESPACE HIDUSB
-#include "hid.c"

+ 18 - 0
src/hidapi/linux/.gitignore

@@ -0,0 +1,18 @@
+Debug
+Release
+*.exp
+*.ilk
+*.lib
+*.suo
+*.vcproj.*
+*.ncb
+*.suo
+*.dll
+*.pdb
+*.o
+*.so
+hidtest-hidraw
+.deps
+.libs
+*.lo
+*.la

+ 38 - 0
src/hidapi/linux/CMakeLists.txt

@@ -0,0 +1,38 @@
+cmake_minimum_required(VERSION 3.6.3 FATAL_ERROR)
+
+add_library(hidapi_hidraw
+    ${HIDAPI_PUBLIC_HEADERS}
+    hid.c
+)
+target_link_libraries(hidapi_hidraw PUBLIC hidapi_include)
+
+find_package(Threads REQUIRED)
+
+include(FindPkgConfig)
+pkg_check_modules(libudev REQUIRED IMPORTED_TARGET libudev)
+
+target_link_libraries(hidapi_hidraw PRIVATE PkgConfig::libudev Threads::Threads)
+
+set_target_properties(hidapi_hidraw
+    PROPERTIES
+        EXPORT_NAME "hidraw"
+        OUTPUT_NAME "hidapi-hidraw"
+        VERSION ${PROJECT_VERSION}
+        SOVERSION ${PROJECT_VERSION_MAJOR}
+        PUBLIC_HEADER "${HIDAPI_PUBLIC_HEADERS}"
+)
+
+# compatibility with find_package()
+add_library(hidapi::hidraw ALIAS hidapi_hidraw)
+# compatibility with raw library link
+add_library(hidapi-hidraw ALIAS hidapi_hidraw)
+
+if(HIDAPI_INSTALL_TARGETS)
+    install(TARGETS hidapi_hidraw EXPORT hidapi
+        LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}"
+        ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}"
+        PUBLIC_HEADER DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/hidapi"
+    )
+endif()
+
+hidapi_configure_pc("${PROJECT_ROOT}/pc/hidapi-hidraw.pc.in")

+ 5 - 12
src/hidapi/linux/Makefile-manual

@@ -13,23 +13,19 @@ libs: libhidapi-hidraw.so
 CC       ?= gcc
 CFLAGS   ?= -Wall -g -fpic
 
-CXX      ?= g++
-CXXFLAGS ?= -Wall -g -fpic
-
 LDFLAGS  ?= -Wall -g
 
 
-COBJS     = hid.o
-CPPOBJS   = ../hidtest/hidtest.o
-OBJS      = $(COBJS) $(CPPOBJS)
+COBJS     = hid.o ../hidtest/test.o
+OBJS      = $(COBJS)
 LIBS_UDEV = `pkg-config libudev --libs` -lrt
 LIBS      = $(LIBS_UDEV)
 INCLUDES ?= -I../hidapi `pkg-config libusb-1.0 --cflags`
 
 
 # Console Test Program
-hidtest-hidraw: $(COBJS) $(CPPOBJS)
-	$(CXX) $(LDFLAGS) $^ $(LIBS_UDEV) -o $@
+hidtest-hidraw: $(COBJS)
+	$(CC) $(LDFLAGS) $^ $(LIBS_UDEV) -o $@
 
 # Shared Libs
 libhidapi-hidraw.so: $(COBJS)
@@ -39,11 +35,8 @@ libhidapi-hidraw.so: $(COBJS)
 $(COBJS): %.o: %.c
 	$(CC) $(CFLAGS) -c $(INCLUDES) $< -o $@
 
-$(CPPOBJS): %.o: %.cpp
-	$(CXX) $(CXXFLAGS) -c $(INCLUDES) $< -o $@
-
 
 clean:
-	rm -f $(OBJS) hidtest-hidraw libhidapi-hidraw.so ../hidtest/hidtest.o
+	rm -f $(OBJS) hidtest-hidraw libhidapi-hidraw.so $(COBJS)
 
 .PHONY: clean libs

+ 0 - 59
src/hidapi/linux/README.txt

@@ -1,59 +0,0 @@
-
-There are two implementations of HIDAPI for Linux. One (linux/hid.c) uses the
-Linux hidraw driver, and the other (libusb/hid.c) uses libusb. Which one you
-use depends on your application. Complete functionality of the hidraw
-version depends on patches to the Linux kernel which are not currently in
-the mainline. These patches have to do with sending and receiving feature
-reports. The libusb implementation uses libusb to talk directly to the
-device, bypassing any Linux HID driver. The disadvantage of the libusb
-version is that it will only work with USB devices, while the hidraw
-implementation will work with Bluetooth devices as well.
-
-To use HIDAPI, simply drop either linux/hid.c or libusb/hid.c into your
-application and build using the build parameters in the Makefile.
-
-
-Libusb Implementation notes
-----------------------------
-For the libusb implementation, libusb-1.0 must be installed. Libusb 1.0 is
-different than the legacy libusb 0.1 which is installed on many systems. To
-install libusb-1.0 on Ubuntu and other Debian-based systems, run:
-	sudo apt-get install libusb-1.0-0-dev
-
-
-Hidraw Implementation notes
-----------------------------
-For the hidraw implementation, libudev headers and libraries are required to
-build hidapi programs.  To install libudev libraries on Ubuntu,
-and other Debian-based systems, run:
-	sudo apt-get install libudev-dev
-
-On Redhat-based systems, run the following as root:
-	yum install libudev-devel
-
-Unfortunately, the hidraw driver, which the linux version of hidapi is based
-on, contains bugs in kernel versions < 2.6.36, which the client application
-should be aware of.
-
-Bugs (hidraw implementation only):
------------------------------------
-On Kernel versions < 2.6.34, if your device uses numbered reports, an extra
-byte will be returned at the beginning of all reports returned from read()
-for hidraw devices. This is worked around in the library. No action should be
-necessary in the client library.
-
-On Kernel versions < 2.6.35, reports will only be sent using a Set_Report
-transfer on the CONTROL endpoint. No data will ever be sent on an Interrupt
-Out endpoint if one exists. This is fixed in 2.6.35. In 2.6.35, OUTPUT
-reports will be sent to the device on the first INTERRUPT OUT endpoint if it
-exists; If it does not exist, OUTPUT reports will be sent on the CONTROL
-endpoint.
-
-On Kernel versions < 2.6.36, add an extra byte containing the report number
-to sent reports if numbered reports are used, and the device does not
-contain an INTERRPUT OUT endpoint for OUTPUT transfers.  For example, if
-your device uses numbered reports and wants to send {0x2 0xff 0xff 0xff} to
-the device (0x2 is the report number), you must send {0x2 0x2 0xff 0xff
-0xff}. If your device has the optional Interrupt OUT endpoint, this does not
-apply (but really on 2.6.35 only, because 2.6.34 won't use the interrupt
-out endpoint).

File diff suppressed because it is too large
+ 631 - 263
src/hidapi/linux/hid.c


+ 0 - 3
src/hidapi/linux/hidraw.cpp

@@ -1,3 +0,0 @@
-
-#define NAMESPACE HIDRAW
-#include "hid.c"

+ 5 - 0
src/hidapi/m4/.gitignore

@@ -0,0 +1,5 @@
+# Ignore All, except pkg.m4, and of course this file.
+*
+!.gitignore
+!pkg.m4
+!ax_pthread.m4

+ 17 - 0
src/hidapi/mac/.gitignore

@@ -0,0 +1,17 @@
+Debug
+Release
+*.exp
+*.ilk
+*.lib
+*.suo
+*.vcproj.*
+*.ncb
+*.suo
+*.dll
+*.pdb
+*.o
+hidapi-hidtest
+.deps
+.libs
+*.la
+*.lo

+ 48 - 0
src/hidapi/mac/CMakeLists.txt

@@ -0,0 +1,48 @@
+cmake_minimum_required(VERSION 3.4.3 FATAL_ERROR)
+
+list(APPEND HIDAPI_PUBLIC_HEADERS "hidapi_darwin.h")
+
+add_library(hidapi_darwin
+    ${HIDAPI_PUBLIC_HEADERS}
+    hid.c
+)
+
+find_package(Threads REQUIRED)
+
+target_link_libraries(hidapi_darwin
+    PUBLIC hidapi_include
+    PRIVATE Threads::Threads
+    PRIVATE "-framework IOKit" "-framework CoreFoundation" "-framework AppKit"
+)
+
+set_target_properties(hidapi_darwin
+    PROPERTIES
+        EXPORT_NAME "darwin"
+        OUTPUT_NAME "hidapi"
+        VERSION ${PROJECT_VERSION}
+        SOVERSION ${PROJECT_VERSION_MAJOR}
+        MACHO_COMPATIBILITY_VERSION ${PROJECT_VERSION_MAJOR}
+        FRAMEWORK_VERSION ${PROJECT_VERSION_MAJOR}
+        PUBLIC_HEADER "${HIDAPI_PUBLIC_HEADERS}"
+)
+
+# compatibility with find_package()
+add_library(hidapi::darwin ALIAS hidapi_darwin)
+# compatibility with raw library link
+add_library(hidapi ALIAS hidapi_darwin)
+
+set(PUBLIC_HEADER_DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}")
+if(NOT CMAKE_FRAMEWORK)
+    set(PUBLIC_HEADER_DESTINATION "${PUBLIC_HEADER_DESTINATION}/hidapi")
+endif()
+
+if(HIDAPI_INSTALL_TARGETS)
+    install(TARGETS hidapi_darwin EXPORT hidapi
+        LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}"
+        ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}"
+        FRAMEWORK DESTINATION "${CMAKE_INSTALL_LIBDIR}"
+        PUBLIC_HEADER DESTINATION "${PUBLIC_HEADER_DESTINATION}"
+    )
+endif()
+
+hidapi_configure_pc("${PROJECT_ROOT}/pc/hidapi.pc.in")

+ 6 - 11
src/hidapi/mac/Makefile-manual

@@ -9,24 +9,19 @@
 all: hidtest
 
 CC=gcc
-CXX=g++
-COBJS=hid.o
-CPPOBJS=../hidtest/hidtest.o
-OBJS=$(COBJS) $(CPPOBJS)
-CFLAGS+=-I../hidapi -Wall -g -c 
-LIBS=-framework IOKit -framework CoreFoundation
+COBJS=hid.o ../hidtest/test.o
+OBJS=$(COBJS)
+CFLAGS+=-I../hidapi -I. -Wall -g -c
+LIBS=-framework IOKit -framework CoreFoundation -framework AppKit
 
 
 hidtest: $(OBJS)
-	g++ -Wall -g $^ $(LIBS) -o hidtest
+	$(CC) -Wall -g $^ $(LIBS) -o hidtest
 
 $(COBJS): %.o: %.c
 	$(CC) $(CFLAGS) $< -o $@
 
-$(CPPOBJS): %.o: %.cpp
-	$(CXX) $(CFLAGS) $< -o $@
-
 clean:
-	rm -f *.o hidtest $(CPPOBJS)
+	rm -f *.o hidtest
 
 .PHONY: clean

File diff suppressed because it is too large
+ 504 - 345
src/hidapi/mac/hid.c


+ 98 - 0
src/hidapi/mac/hidapi_darwin.h

@@ -0,0 +1,98 @@
+/*******************************************************
+ HIDAPI - Multi-Platform library for
+ communication with HID devices.
+
+ libusb/hidapi Team
+
+ Copyright 2022, All Rights Reserved.
+
+ At the discretion of the user of this library,
+ this software may be licensed under the terms of the
+ GNU General Public License v3, a BSD-Style license, or the
+ original HIDAPI license as outlined in the LICENSE.txt,
+ LICENSE-gpl3.txt, LICENSE-bsd.txt, and LICENSE-orig.txt
+ files located at the root of the source distribution.
+ These files may also be found in the public source
+ code repository located at:
+        https://github.com/libusb/hidapi .
+********************************************************/
+
+/** @file
+ * @defgroup API hidapi API
+
+ * Since version 0.12.0, @ref HID_API_VERSION >= HID_API_MAKE_VERSION(0, 12, 0)
+ */
+
+#ifndef HIDAPI_DARWIN_H__
+#define HIDAPI_DARWIN_H__
+
+#include <stdint.h>
+
+#include "hidapi.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+		/** @brief Get the location ID for a HID device.
+
+			Since version 0.12.0, @ref HID_API_VERSION >= HID_API_MAKE_VERSION(0, 12, 0)
+
+			@ingroup API
+			@param dev A device handle returned from hid_open().
+			@param location_id The device's location ID on return.
+
+			@returns
+				This function returns 0 on success and -1 on error.
+		*/
+		int HID_API_EXPORT_CALL hid_darwin_get_location_id(hid_device *dev, uint32_t *location_id);
+
+
+		/** @brief Changes the behavior of all further calls to @ref hid_open or @ref hid_open_path.
+
+			By default on Darwin platform all devices opened by HIDAPI with @ref hid_open or @ref hid_open_path
+			are opened in exclusive mode (see kIOHIDOptionsTypeSeizeDevice).
+
+			Since version 0.12.0, @ref HID_API_VERSION >= HID_API_MAKE_VERSION(0, 12, 0)
+
+			@ingroup API
+			@param open_exclusive When set to 0 - all further devices will be opened
+				in non-exclusive mode. Otherwise - all further devices will be opened
+				in exclusive mode.
+
+			@note During the initialisation by @ref hid_init - this property is set to 1 (TRUE).
+			This is done to preserve full backward compatibility with previous behavior.
+
+			@note Calling this function before @ref hid_init or after @ref hid_exit has no effect.
+		*/
+		void HID_API_EXPORT_CALL hid_darwin_set_open_exclusive(int open_exclusive);
+
+		/** @brief Getter for option set by @ref hid_darwin_set_open_exclusive.
+
+			Since version 0.12.0, @ref HID_API_VERSION >= HID_API_MAKE_VERSION(0, 12, 0)
+
+			@ingroup API
+			@return 1 if all further devices will be opened in exclusive mode.
+
+			@note Value returned by this function before calling to @ref hid_init or after @ref hid_exit
+			is not reliable.
+		*/
+		int HID_API_EXPORT_CALL hid_darwin_get_open_exclusive(void);
+
+		/** @brief Check how the device was opened.
+
+			Since version 0.12.0, @ref HID_API_VERSION >= HID_API_MAKE_VERSION(0, 12, 0)
+
+			@ingroup API
+			@param dev A device to get property from.
+
+			@return 1 if the device is opened in exclusive mode, 0 - opened in non-exclusive,
+			-1 - if dev is invalid.
+		*/
+		int HID_API_EXPORT_CALL hid_darwin_is_device_open_exclusive(hid_device *dev);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif

+ 22 - 0
src/hidapi/meson.build

@@ -0,0 +1,22 @@
+project('hidapi', meson_version: '>=0.57.0', version: files('VERSION'))
+
+cmake = import('cmake')
+
+hidapi_build_options = cmake.subproject_options()
+hidapi_build_options.set_install(true)
+
+hidapi_build = cmake.subproject('hidapi_build_cmake', options: hidapi_build_options)
+
+if (hidapi_build.target_list().contains('hidapi_winapi'))
+	hidapi_winapi_dep = hidapi_build.dependency('hidapi_winapi')
+	hidapi_dep = hidapi_winapi_dep
+elif (hidapi_build.target_list().contains('hidapi_darwin'))
+	hidapi_darwin_dep = hidapi_build.dependency('hidapi_darwin')
+	hidapi_dep = hidapi_darwin_dep
+elif (hidapi_build.target_list().contains('hidapi_hidraw'))
+	hidapi_hidraw_dep = hidapi_build.dependency('hidapi_hidraw')
+	hidapi_dep = hidapi_hidraw_dep
+elif (hidapi_build.target_list().contains('hidapi_libusb'))
+	hidapi_libusb_dep = hidapi_build.dependency('hidapi_libusb')
+	hidapi_dep = hidapi_libusb_dep
+endif

+ 1 - 0
src/hidapi/pc/.gitignore

@@ -0,0 +1 @@
+*.pc

+ 1 - 0
src/hidapi/pc/hidapi-hidraw.pc.in

@@ -5,6 +5,7 @@ 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

+ 1 - 0
src/hidapi/pc/hidapi-libusb.pc.in

@@ -5,6 +5,7 @@ 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

+ 1 - 0
src/hidapi/pc/hidapi.pc.in

@@ -5,6 +5,7 @@ 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

+ 193 - 0
src/hidapi/src/CMakeLists.txt

@@ -0,0 +1,193 @@
+get_filename_component(PROJECT_ROOT "${CMAKE_CURRENT_LIST_DIR}/.." ABSOLUTE)
+
+# Read version from file
+file(READ "${PROJECT_ROOT}/VERSION" RAW_VERSION_STR)
+string(REGEX MATCH "^([0-9]+\\.[0-9]+\\.[0-9]+)(.*)" VERSION_STR "${RAW_VERSION_STR}")
+
+if(NOT VERSION_STR)
+    message(FATAL_ERROR "Broken VERSION file, couldn't parse '${PROJECT_ROOT}/VERSION' with content: '${RAW_VERSION_STR}'")
+endif()
+
+set(VERSION "${CMAKE_MATCH_1}")
+string(STRIP "${CMAKE_MATCH_2}" VERSION_SUFFIX)
+# compatibility with find_package() vs add_subdirectory
+set(hidapi_VERSION "${VERSION}" PARENT_SCOPE)
+#
+
+if(DEFINED HIDAPI_PRINT_VERSION AND HIDAPI_PRINT_VERSION)
+    set(HIDAPI_PRINT_VERSION "hidapi: v${VERSION}")
+    if(VERSION_SUFFIX)
+        set(HIDAPI_PRINT_VERSION "${HIDAPI_PRINT_VERSION} (${VERSION_SUFFIX})")
+    endif()
+    message(STATUS "${HIDAPI_PRINT_VERSION}")
+endif()
+
+project(hidapi VERSION "${VERSION}" LANGUAGES C)
+
+# Defaults and required options
+
+if(NOT DEFINED HIDAPI_WITH_TESTS)
+    set(HIDAPI_WITH_TESTS OFF)
+endif()
+if(NOT DEFINED BUILD_SHARED_LIBS)
+    set(BUILD_SHARED_LIBS ON)
+endif()
+if(NOT DEFINED HIDAPI_INSTALL_TARGETS)
+    set(HIDAPI_INSTALL_TARGETS OFF)
+endif()
+if(NOT DEFINED CMAKE_POSITION_INDEPENDENT_CODE)
+    set(CMAKE_POSITION_INDEPENDENT_CODE ON)
+endif()
+
+get_directory_property(IS_EXCLUDE_FROM_ALL EXCLUDE_FROM_ALL)
+if(IS_EXCLUDE_FROM_ALL)
+    if(HIDAPI_INSTALL_TARGETS)
+        message(WARNING "Installing EXCLUDE_FROM_ALL targets in an undefined behavior in CMake.\nDon't add 'hidapi' sundirectory with 'EXCLUDE_FROM_ALL' property, or don't set 'HIDAPI_INSTALL_TARGETS' to TRUE.")
+    endif()
+endif()
+
+# Helper(s)
+
+function(hidapi_configure_pc PC_IN_FILE)
+    file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/pc")
+
+    set(VERSION "${VERSION}${VERSION_SUFFIX}")
+    set(prefix "${CMAKE_INSTALL_PREFIX}")
+    set(exec_prefix "\${prefix}")
+    if(IS_ABSOLUTE "${CMAKE_INSTALL_LIBDIR}")
+        set(libdir "${CMAKE_INSTALL_LIBDIR}")
+    else()
+        set(libdir "\${exec_prefix}/${CMAKE_INSTALL_LIBDIR}")
+    endif()
+    if(IS_ABSOLUTE "${CMAKE_INSTALL_INCLUDEDIR}")
+        set(includedir "${CMAKE_INSTALL_INCLUDEDIR}")
+    else()
+        set(includedir "\${prefix}/${CMAKE_INSTALL_INCLUDEDIR}")
+    endif()
+
+    get_filename_component(PC_IN_FILENAME "${PC_IN_FILE}" NAME_WE)
+    set(PC_FILE "${CMAKE_CURRENT_BINARY_DIR}/pc/${PC_IN_FILENAME}.pc")
+    configure_file("${PC_IN_FILE}" "${PC_FILE}" @ONLY)
+
+    install(FILES "${PC_FILE}" DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig/")
+endfunction()
+
+# The library
+
+if(HIDAPI_INSTALL_TARGETS)
+    include(GNUInstallDirs)
+endif()
+
+add_library(hidapi_include INTERFACE)
+target_include_directories(hidapi_include INTERFACE
+    "$<BUILD_INTERFACE:${PROJECT_ROOT}/hidapi>"
+)
+if(APPLE AND CMAKE_FRAMEWORK)
+    # FIXME: https://github.com/libusb/hidapi/issues/492: it is untrivial to set the include path for Framework correctly
+else()
+    target_include_directories(hidapi_include INTERFACE
+        "$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}/hidapi>"
+    )
+endif()
+set_target_properties(hidapi_include PROPERTIES EXPORT_NAME "include")
+set(HIDAPI_PUBLIC_HEADERS "${PROJECT_ROOT}/hidapi/hidapi.h")
+
+add_library(hidapi::include ALIAS hidapi_include)
+
+if(HIDAPI_INSTALL_TARGETS)
+    install(TARGETS hidapi_include EXPORT hidapi)
+endif()
+
+set(EXPORT_ALIAS)
+set(EXPORT_COMPONENTS)
+
+set(HIDAPI_NEED_EXPORT_THREADS FALSE)
+set(HIDAPI_NEED_EXPORT_LIBUSB FALSE)
+set(HIDAPI_NEED_EXPORT_LIBUDEV FALSE)
+set(HIDAPI_NEED_EXPORT_ICONV FALSE)
+
+if(WIN32)
+    target_include_directories(hidapi_include INTERFACE
+        "$<BUILD_INTERFACE:${PROJECT_ROOT}/windows>"
+    )
+    add_subdirectory("${PROJECT_ROOT}/windows" windows)
+    set(EXPORT_ALIAS winapi)
+    list(APPEND EXPORT_COMPONENTS winapi)
+elseif(APPLE)
+    target_include_directories(hidapi_include INTERFACE
+        "$<BUILD_INTERFACE:${PROJECT_ROOT}/mac>"
+    )
+    add_subdirectory("${PROJECT_ROOT}/mac" mac)
+    set(EXPORT_ALIAS darwin)
+    list(APPEND EXPORT_COMPONENTS darwin)
+    if(NOT BUILD_SHARED_LIBS)
+        set(HIDAPI_NEED_EXPORT_THREADS TRUE)
+    endif()
+else()
+    if(NOT DEFINED HIDAPI_WITH_LIBUSB)
+        set(HIDAPI_WITH_LIBUSB ON)
+    endif()
+    if(CMAKE_SYSTEM_NAME MATCHES "Linux")
+        if(NOT DEFINED HIDAPI_WITH_HIDRAW)
+            set(HIDAPI_WITH_HIDRAW ON)
+        endif()
+        if(HIDAPI_WITH_HIDRAW)
+            add_subdirectory("${PROJECT_ROOT}/linux" linux)
+            list(APPEND EXPORT_COMPONENTS hidraw)
+            set(EXPORT_ALIAS hidraw)
+            if(NOT BUILD_SHARED_LIBS)
+                set(HIDAPI_NEED_EXPORT_THREADS TRUE)
+                set(HIDAPI_NEED_EXPORT_LIBUDEV TRUE)
+            endif()
+        endif()
+    else()
+        set(HIDAPI_WITH_LIBUSB ON)
+    endif()
+    if(HIDAPI_WITH_LIBUSB)
+        target_include_directories(hidapi_include INTERFACE
+            "$<BUILD_INTERFACE:${PROJECT_ROOT}/libusb>"
+        )
+        if(NOT DEFINED HIDAPI_NO_ICONV)
+            set(HIDAPI_NO_ICONV OFF)
+        endif()
+        add_subdirectory("${PROJECT_ROOT}/libusb" libusb)
+        list(APPEND EXPORT_COMPONENTS libusb)
+        if(NOT EXPORT_ALIAS)
+            set(EXPORT_ALIAS libusb)
+        endif()
+        if(NOT BUILD_SHARED_LIBS)
+            set(HIDAPI_NEED_EXPORT_THREADS TRUE)
+            if(NOT TARGET usb-1.0)
+                set(HIDAPI_NEED_EXPORT_LIBUSB TRUE)
+            endif()
+        endif()
+    elseif(NOT TARGET hidapi_hidraw)
+        message(FATAL_ERROR "Select at least one option to build: HIDAPI_WITH_LIBUSB or HIDAPI_WITH_HIDRAW")
+    endif()
+endif()
+
+add_library(hidapi::hidapi ALIAS hidapi_${EXPORT_ALIAS})
+
+if(HIDAPI_INSTALL_TARGETS)
+    include(CMakePackageConfigHelpers)
+    set(EXPORT_DENERATED_LOCATION "${CMAKE_BINARY_DIR}/export_generated")
+    set(EXPORT_DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/hidapi")
+    write_basic_package_version_file("${EXPORT_DENERATED_LOCATION}/hidapi-config-version.cmake"
+        COMPATIBILITY SameMajorVersion
+    )
+    configure_package_config_file("cmake/hidapi-config.cmake.in" "${EXPORT_DENERATED_LOCATION}/hidapi-config.cmake"
+        INSTALL_DESTINATION "${EXPORT_DESTINATION}"
+        NO_SET_AND_CHECK_MACRO
+    )
+
+    install(EXPORT hidapi
+        DESTINATION "${EXPORT_DESTINATION}"
+        NAMESPACE hidapi::
+        FILE "libhidapi.cmake"
+    )
+    install(FILES
+            "${EXPORT_DENERATED_LOCATION}/hidapi-config-version.cmake"
+            "${EXPORT_DENERATED_LOCATION}/hidapi-config.cmake"
+        DESTINATION "${EXPORT_DESTINATION}"
+    )
+endif()

+ 61 - 0
src/hidapi/src/cmake/hidapi-config.cmake.in

@@ -0,0 +1,61 @@
+@PACKAGE_INIT@
+
+set(hidapi_VERSION_MAJOR "@hidapi_VERSION_MAJOR@")
+set(hidapi_VERSION_MINOR "@hidapi_VERSION_MINOR@")
+set(hidapi_VERSION_PATCH "@hidapi_VERSION_PATCH@")
+set(hidapi_VERSION "@hidapi_VERSION@")
+set(hidapi_VERSION_STR "@hidapi_VERSION@@VERSION_SUFFIX@")
+
+set(hidapi_FOUND FALSE)
+
+set(HIDAPI_NEED_EXPORT_THREADS @HIDAPI_NEED_EXPORT_THREADS@)
+set(HIDAPI_NEED_EXPORT_LIBUSB @HIDAPI_NEED_EXPORT_LIBUSB@)
+set(HIDAPI_NEED_EXPORT_LIBUDEV @HIDAPI_NEED_EXPORT_LIBUDEV@)
+set(HIDAPI_NEED_EXPORT_ICONV @HIDAPI_NEED_EXPORT_ICONV@)
+
+if(HIDAPI_NEED_EXPORT_THREADS)
+  if(CMAKE_VERSION VERSION_LESS 3.4.3)
+    message(FATAL_ERROR "This file relies on consumers using CMake 3.4.3 or greater.")
+  endif()
+  find_package(Threads REQUIRED)
+endif()
+
+if(HIDAPI_NEED_EXPORT_LIBUSB OR HIDAPI_NEED_EXPORT_LIBUDEV)
+  if(CMAKE_VERSION VERSION_LESS 3.6.3)
+    message(FATAL_ERROR "This file relies on consumers using CMake 3.6.3 or greater.")
+  endif()
+  find_package(PkgConfig)
+  if(HIDAPI_NEED_EXPORT_LIBUSB)
+    pkg_check_modules(libusb REQUIRED IMPORTED_TARGET libusb-1.0>=1.0.9)
+  endif()
+  if(HIDAPI_NEED_EXPORT_LIBUDEV)
+    pkg_check_modules(libudev REQUIRED IMPORTED_TARGET libudev)
+  endif()
+endif()
+
+if(HIDAPI_NEED_EXPORT_ICONV)
+  if(CMAKE_VERSION VERSION_LESS 3.11)
+    message(WARNING "HIDAPI requires CMake target Iconv::Iconv, make sure to provide it")
+  else()
+    find_package(Iconv REQUIRED)
+  endif()
+endif()
+
+include("${CMAKE_CURRENT_LIST_DIR}/libhidapi.cmake")
+
+set(hidapi_FOUND TRUE)
+
+foreach(_component @EXPORT_COMPONENTS@)
+  if(TARGET hidapi::${_component})
+    set(hidapi_${_component}_FOUND TRUE)
+  endif()
+endforeach()
+
+check_required_components(hidapi)
+
+if(NOT TARGET hidapi::hidapi)
+  add_library(hidapi::hidapi INTERFACE IMPORTED)
+  set_target_properties(hidapi::hidapi PROPERTIES
+    INTERFACE_LINK_LIBRARIES hidapi::@EXPORT_ALIAS@
+  )
+endif()

+ 2 - 0
src/hidapi/subprojects/README.md

@@ -0,0 +1,2 @@
+This folder is used only to support [meson.build](../meson.build) `subproject` command
+which would only look for a subproject in a "subprojects" directory.

+ 10 - 0
src/hidapi/subprojects/hidapi_build_cmake/CMakeLists.txt

@@ -0,0 +1,10 @@
+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)

+ 20 - 0
src/hidapi/testgui/.gitignore

@@ -0,0 +1,20 @@
+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

+ 1 - 1
src/hidapi/testgui/Makefile.mingw

@@ -14,7 +14,7 @@ COBJS=../windows/hid.o
 CPPOBJS=test.o
 OBJS=$(COBJS) $(CPPOBJS)
 CFLAGS=-I../hidapi -I../../hidapi-externals/fox/include -g -c
-LIBS= -mwindows -lsetupapi -L../../hidapi-externals/fox/lib -Wl,-Bstatic -lFOX-1.6 -Wl,-Bdynamic -lgdi32 -Wl,--enable-auto-import -static-libgcc -static-libstdc++ -lkernel32
+LIBS= -mwindows -L../../hidapi-externals/fox/lib -Wl,-Bstatic -lFOX-1.6 -Wl,-Bdynamic -lgdi32 -Wl,--enable-auto-import -static-libgcc -static-libstdc++ -lkernel32
 
 
 hidapi-testgui: $(OBJS)

+ 2 - 1
src/hidapi/testgui/copy_to_bundle.sh

@@ -77,9 +77,10 @@ function copydeps {
 }
 
 rm -f $EXEPATH/*
+mkdir -p $EXEPATH
 
 # Copy the binary into the bundle. Use ../libtool to do this if it's
-# available beacuse if $EXE_NAME was built with autotools, it will be
+# 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

+ 0 - 134
src/hidapi/testgui/mac_support.cpp

@@ -1,134 +0,0 @@
-/*******************************
- Mac support for HID Test GUI
- 
- Alan Ott
- Signal 11 Software
-
- Some of this code is from Apple Documentation, most notably
- http://developer.apple.com/legacy/mac/library/documentation/AppleScript/Conceptual/AppleEvents/AppleEvents.pdf 
-*******************************/
-
-#include <Carbon/Carbon.h>
-#include <fx.h>
-
-
-extern FXMainWindow *g_main_window;
-
-static pascal OSErr HandleQuitMessage(const AppleEvent *theAppleEvent, AppleEvent 
-									  *reply, long handlerRefcon) 
-{
-	puts("Quitting\n");
-	FXApp::instance()->exit();
-	return 0;
-}
-
-static pascal OSErr HandleReopenMessage(const AppleEvent *theAppleEvent, AppleEvent 
-									  *reply, long handlerRefcon) 
-{
-	puts("Showing");
-	g_main_window->show();
-	return 0;
-}
-
-static pascal OSErr HandleWildCardMessage(const AppleEvent *theAppleEvent, AppleEvent 
-									  *reply, long handlerRefcon) 
-{
-	puts("WildCard\n");
-	return 0;
-}
-
-OSStatus AEHandler(EventHandlerCallRef inCaller, EventRef inEvent, void* inRefcon) 
-{ 
-    Boolean     release = false; 
-    EventRecord eventRecord; 
-    OSErr       ignoreErrForThisSample; 
-	
-    // Events of type kEventAppleEvent must be removed from the queue 
-    //  before being passed to AEProcessAppleEvent. 
-    if (IsEventInQueue(GetMainEventQueue(), inEvent)) 
-    { 
-        // RemoveEventFromQueue will release the event, which will 
-        //  destroy it if we don't retain it first. 
-        RetainEvent(inEvent); 
-        release = true; 
-        RemoveEventFromQueue(GetMainEventQueue(), inEvent); 
-    } 
-    // Convert the event ref to the type AEProcessAppleEvent expects. 
-    ConvertEventRefToEventRecord(inEvent, &eventRecord); 
-    ignoreErrForThisSample = AEProcessAppleEvent(&eventRecord); 
-    if (release) 
-        ReleaseEvent(inEvent); 
-    // This Carbon event has been handled, even if no AppleEvent handlers 
-    //  were installed for the Apple event. 
-    return noErr; 
-}
-
-static void HandleEvent(EventRecord *event) 
-{ 
-	//printf("What: %d message %x\n", event->what, event->message);
-	if (event->what == osEvt) {
-		if (((event->message >> 24) & 0xff) == suspendResumeMessage) {
-			if (event->message & resumeFlag) {
-				g_main_window->show();				
-			}
-		}
-	}
-
-#if 0
-    switch (event->what) 
-    { 
-        case mouseDown: 
-            //HandleMouseDown(event); 
-            break; 
-        case keyDown: 
-        case autoKey: 
-            //HandleKeyPress(event); 
-            break; 
-        case kHighLevelEvent: 
-			puts("Calling ProcessAppleEvent\n");
-            AEProcessAppleEvent(event); 
-            break; 
-    } 
-#endif
-} 
-
-void
-init_apple_message_system()
-{
-	OSErr err;
-	static const EventTypeSpec appleEvents[] = 
-	{
-		{ kEventClassAppleEvent, kEventAppleEvent }
-	};
-	
-	/* Install the handler for Apple Events */
-	InstallApplicationEventHandler(NewEventHandlerUPP(AEHandler), 
-	              GetEventTypeCount(appleEvents), appleEvents, 0, NULL); 
-
-	/* Install handlers for the individual Apple Events that come
-	   from the Dock icon: the Reopen (click), and the Quit messages. */
-	err = AEInstallEventHandler(kCoreEventClass, kAEQuitApplication, 
-	              NewAEEventHandlerUPP(HandleQuitMessage), 0, false);
-	err = AEInstallEventHandler(kCoreEventClass, kAEReopenApplication, 
-	              NewAEEventHandlerUPP(HandleReopenMessage), 0, false);
-#if 0
-	// Left as an example of a wild card match.
-	err = AEInstallEventHandler(kCoreEventClass, typeWildCard, 
-	              NewAEEventHandlerUPP(HandleWildMessage), 0, false);
-#endif
-}
-
-void
-check_apple_events()
-{
-	RgnHandle       cursorRgn = NULL; 
-	Boolean         gotEvent=TRUE; 
-	EventRecord     event; 
-
-	while (gotEvent) { 
-		gotEvent = WaitNextEvent(everyEvent, &event, 0L/*timeout*/, cursorRgn); 
-		if (gotEvent) { 
-			HandleEvent(&event); 
-		} 
-	}
-}

+ 11 - 2
src/hidapi/testgui/mac_support_cocoa.m

@@ -8,10 +8,19 @@
 #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
+@interface MyAppDelegate : NSObject<NSApplicationDelegate>
 {
 } 
 @end
@@ -77,7 +86,7 @@ check_apple_events()
 
 	NSAutoreleasePool *pool = [NSAutoreleasePool new];
 	while (1) {
-		NSEvent* event = [NSApp nextEventMatchingMask:NSAnyEventMask
+		NSEvent* event = [NSApp nextEventMatchingMask:NSEventMaskAny
 		                        untilDate:nil
                                         inMode:NSDefaultRunLoopMode
                                         dequeue:YES];

+ 0 - 2
src/hidapi/testgui/start.sh

@@ -1,2 +0,0 @@
-#!/bin/bash
-xterm -e /Users/alan/work/hidapi/testgui/TestGUI.app/Contents/MacOS/tg

+ 20 - 20
src/hidapi/testgui/testgui.sln

@@ -1,20 +1,20 @@
-
-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
+
+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

+ 217 - 217
src/hidapi/testgui/testgui.vcproj

@@ -1,217 +1,217 @@
-<?xml version="1.0" encoding="Windows-1252"?>
-<VisualStudioProject
-	ProjectType="Visual C++"
-	Version="9.00"
-	Name="testgui"
-	ProjectGUID="{08769AC3-785A-4DDC-BFC7-1775414B7AB7}"
-	RootNamespace="testgui"
-	Keyword="Win32Proj"
-	TargetFrameworkVersion="196613"
-	>
-	<Platforms>
-		<Platform
-			Name="Win32"
-		/>
-	</Platforms>
-	<ToolFiles>
-	</ToolFiles>
-	<Configurations>
-		<Configuration
-			Name="Debug|Win32"
-			OutputDirectory="$(SolutionDir)$(ConfigurationName)"
-			IntermediateDirectory="$(ConfigurationName)"
-			ConfigurationType="1"
-			CharacterSet="1"
-			>
-			<Tool
-				Name="VCPreBuildEventTool"
-			/>
-			<Tool
-				Name="VCCustomBuildTool"
-			/>
-			<Tool
-				Name="VCXMLDataGeneratorTool"
-			/>
-			<Tool
-				Name="VCWebServiceProxyGeneratorTool"
-			/>
-			<Tool
-				Name="VCMIDLTool"
-			/>
-			<Tool
-				Name="VCCLCompilerTool"
-				Optimization="0"
-				AdditionalIncludeDirectories="&quot;..\..\hidapi-externals\fox\include&quot;;..\hidapi"
-				PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS"
-				MinimalRebuild="true"
-				BasicRuntimeChecks="3"
-				RuntimeLibrary="3"
-				UsePrecompiledHeader="0"
-				WarningLevel="3"
-				DebugInformationFormat="4"
-			/>
-			<Tool
-				Name="VCManagedResourceCompilerTool"
-			/>
-			<Tool
-				Name="VCResourceCompilerTool"
-			/>
-			<Tool
-				Name="VCPreLinkEventTool"
-			/>
-			<Tool
-				Name="VCLinkerTool"
-				AdditionalDependencies="setupapi.lib fox-1.6.lib"
-				OutputFile="$(ProjectName).exe"
-				LinkIncremental="2"
-				AdditionalLibraryDirectories="..\hidapi\objfre_wxp_x86\i386;&quot;..\..\hidapi-externals\fox\lib&quot;"
-				GenerateDebugInformation="true"
-				SubSystem="2"
-				EntryPointSymbol="mainCRTStartup"
-				TargetMachine="1"
-			/>
-			<Tool
-				Name="VCALinkTool"
-			/>
-			<Tool
-				Name="VCManifestTool"
-			/>
-			<Tool
-				Name="VCXDCMakeTool"
-			/>
-			<Tool
-				Name="VCBscMakeTool"
-			/>
-			<Tool
-				Name="VCFxCopTool"
-			/>
-			<Tool
-				Name="VCAppVerifierTool"
-			/>
-			<Tool
-				Name="VCPostBuildEventTool"
-				CommandLine=""
-			/>
-		</Configuration>
-		<Configuration
-			Name="Release|Win32"
-			OutputDirectory="$(SolutionDir)$(ConfigurationName)"
-			IntermediateDirectory="$(ConfigurationName)"
-			ConfigurationType="1"
-			CharacterSet="1"
-			WholeProgramOptimization="1"
-			>
-			<Tool
-				Name="VCPreBuildEventTool"
-			/>
-			<Tool
-				Name="VCCustomBuildTool"
-			/>
-			<Tool
-				Name="VCXMLDataGeneratorTool"
-			/>
-			<Tool
-				Name="VCWebServiceProxyGeneratorTool"
-			/>
-			<Tool
-				Name="VCMIDLTool"
-			/>
-			<Tool
-				Name="VCCLCompilerTool"
-				Optimization="2"
-				EnableIntrinsicFunctions="true"
-				AdditionalIncludeDirectories="&quot;..\..\hidapi-externals\fox\include&quot;;..\hidapi"
-				PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS"
-				RuntimeLibrary="2"
-				EnableFunctionLevelLinking="true"
-				UsePrecompiledHeader="0"
-				WarningLevel="3"
-				DebugInformationFormat="3"
-			/>
-			<Tool
-				Name="VCManagedResourceCompilerTool"
-			/>
-			<Tool
-				Name="VCResourceCompilerTool"
-			/>
-			<Tool
-				Name="VCPreLinkEventTool"
-			/>
-			<Tool
-				Name="VCLinkerTool"
-				AdditionalDependencies="setupapi.lib fox-1.6.lib"
-				OutputFile="$(ProjectName).exe"
-				LinkIncremental="1"
-				AdditionalLibraryDirectories="..\hidapi\objfre_wxp_x86\i386;&quot;..\..\hidapi-externals\fox\lib&quot;"
-				GenerateDebugInformation="true"
-				SubSystem="2"
-				OptimizeReferences="2"
-				EnableCOMDATFolding="2"
-				EntryPointSymbol="mainCRTStartup"
-				TargetMachine="1"
-			/>
-			<Tool
-				Name="VCALinkTool"
-			/>
-			<Tool
-				Name="VCManifestTool"
-			/>
-			<Tool
-				Name="VCXDCMakeTool"
-			/>
-			<Tool
-				Name="VCBscMakeTool"
-			/>
-			<Tool
-				Name="VCFxCopTool"
-			/>
-			<Tool
-				Name="VCAppVerifierTool"
-			/>
-			<Tool
-				Name="VCPostBuildEventTool"
-				CommandLine=""
-			/>
-		</Configuration>
-	</Configurations>
-	<References>
-	</References>
-	<Files>
-		<Filter
-			Name="Source Files"
-			Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx"
-			UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"
-			>
-			<File
-				RelativePath="..\windows\hid.c"
-				>
-			</File>
-			<File
-				RelativePath=".\test.cpp"
-				>
-			</File>
-		</Filter>
-		<Filter
-			Name="Header Files"
-			Filter="h;hpp;hxx;hm;inl;inc;xsd"
-			UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"
-			>
-			<File
-				RelativePath="..\hidapi\hidapi.h"
-				>
-			</File>
-		</Filter>
-		<Filter
-			Name="Resource Files"
-			Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav"
-			UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}"
-			>
-		</Filter>
-		<File
-			RelativePath=".\ReadMe.txt"
-			>
-		</File>
-	</Files>
-	<Globals>
-	</Globals>
-</VisualStudioProject>
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+	ProjectType="Visual C++"
+	Version="9.00"
+	Name="testgui"
+	ProjectGUID="{08769AC3-785A-4DDC-BFC7-1775414B7AB7}"
+	RootNamespace="testgui"
+	Keyword="Win32Proj"
+	TargetFrameworkVersion="196613"
+	>
+	<Platforms>
+		<Platform
+			Name="Win32"
+		/>
+	</Platforms>
+	<ToolFiles>
+	</ToolFiles>
+	<Configurations>
+		<Configuration
+			Name="Debug|Win32"
+			OutputDirectory="$(SolutionDir)$(ConfigurationName)"
+			IntermediateDirectory="$(ConfigurationName)"
+			ConfigurationType="1"
+			CharacterSet="1"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				Optimization="0"
+				AdditionalIncludeDirectories="&quot;..\..\hidapi-externals\fox\include&quot;;..\hidapi"
+				PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS"
+				MinimalRebuild="true"
+				BasicRuntimeChecks="3"
+				RuntimeLibrary="3"
+				UsePrecompiledHeader="0"
+				WarningLevel="3"
+				DebugInformationFormat="4"
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLinkerTool"
+				AdditionalDependencies="fox-1.6.lib"
+				OutputFile="$(ProjectName).exe"
+				LinkIncremental="2"
+				AdditionalLibraryDirectories="..\hidapi\objfre_wxp_x86\i386;&quot;..\..\hidapi-externals\fox\lib&quot;"
+				GenerateDebugInformation="true"
+				SubSystem="2"
+				EntryPointSymbol="mainCRTStartup"
+				TargetMachine="1"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCManifestTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCAppVerifierTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+				CommandLine=""
+			/>
+		</Configuration>
+		<Configuration
+			Name="Release|Win32"
+			OutputDirectory="$(SolutionDir)$(ConfigurationName)"
+			IntermediateDirectory="$(ConfigurationName)"
+			ConfigurationType="1"
+			CharacterSet="1"
+			WholeProgramOptimization="1"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				Optimization="2"
+				EnableIntrinsicFunctions="true"
+				AdditionalIncludeDirectories="&quot;..\..\hidapi-externals\fox\include&quot;;..\hidapi"
+				PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS"
+				RuntimeLibrary="2"
+				EnableFunctionLevelLinking="true"
+				UsePrecompiledHeader="0"
+				WarningLevel="3"
+				DebugInformationFormat="3"
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLinkerTool"
+				AdditionalDependencies="fox-1.6.lib"
+				OutputFile="$(ProjectName).exe"
+				LinkIncremental="1"
+				AdditionalLibraryDirectories="..\hidapi\objfre_wxp_x86\i386;&quot;..\..\hidapi-externals\fox\lib&quot;"
+				GenerateDebugInformation="true"
+				SubSystem="2"
+				OptimizeReferences="2"
+				EnableCOMDATFolding="2"
+				EntryPointSymbol="mainCRTStartup"
+				TargetMachine="1"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCManifestTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCAppVerifierTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+				CommandLine=""
+			/>
+		</Configuration>
+	</Configurations>
+	<References>
+	</References>
+	<Files>
+		<Filter
+			Name="Source Files"
+			Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx"
+			UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"
+			>
+			<File
+				RelativePath="..\windows\hid.c"
+				>
+			</File>
+			<File
+				RelativePath=".\test.cpp"
+				>
+			</File>
+		</Filter>
+		<Filter
+			Name="Header Files"
+			Filter="h;hpp;hxx;hm;inl;inc;xsd"
+			UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"
+			>
+			<File
+				RelativePath="..\hidapi\hidapi.h"
+				>
+			</File>
+		</Filter>
+		<Filter
+			Name="Resource Files"
+			Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav"
+			UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}"
+			>
+		</Filter>
+		<File
+			RelativePath=".\ReadMe.txt"
+			>
+		</File>
+	</Files>
+	<Globals>
+	</Globals>
+</VisualStudioProject>

+ 36 - 0
src/hidapi/udev/69-hid.rules

@@ -0,0 +1,36 @@
+# This is a sample udev file for HIDAPI devices which lets unprivileged
+# users who are physically present at the system (not remote users) access
+# HID devices.
+
+# If you are using the libusb implementation of hidapi (libusb/hid.c), then
+# use something like the following line, substituting the VID and PID with
+# those of your device.
+
+# HIDAPI/libusb
+SUBSYSTEMS=="usb", ATTRS{idVendor}=="04d8", ATTRS{idProduct}=="003f", TAG+="uaccess"
+
+# If you are using the hidraw implementation (linux/hid.c), then do something
+# like the following, substituting the VID and PID with your device.
+
+# HIDAPI/hidraw
+KERNEL=="hidraw*", ATTRS{idVendor}=="04d8", ATTRS{idProduct}=="003f", TAG+="uaccess"
+
+# Once done, optionally rename this file for your application, and drop it into
+# /etc/udev/rules.d/.
+# NOTE: these rules must have priorty before 73-seat-late.rules.
+# (Small discussion/explanation in systemd repo:
+#  https://github.com/systemd/systemd/issues/4288#issuecomment-348166161)
+# for example, name the file /etc/udev/rules.d/70-my-application-hid.rules.
+# Then, replug your device or run:
+# sudo udevadm control --reload-rules && sudo udevadm trigger
+
+# Note that the hexadecimal values for VID and PID are case sensitive and
+# must be lower case.
+
+# TAG+="uaccess" only gives permission to physically present users, which
+# is appropriate in most scenarios. If you require access to the device
+# from a remote session (e.g. over SSH), add
+# GROUP="plugdev", MODE="660"
+# to the end of the udev rule lines, add your user to the plugdev group with:
+# usermod -aG plugdev USERNAME
+# then log out and log back in (or restart the system).

+ 0 - 33
src/hidapi/udev/99-hid.rules

@@ -1,33 +0,0 @@
-# This is a sample udev file for HIDAPI devices which changes the permissions
-# to 0666 (world readable/writable) for a specified device on Linux systems.
-
-
-# If you are using the libusb implementation of hidapi (libusb/hid.c), then
-# use something like the following line, substituting the VID and PID with
-# those of your device. Note that for kernels before 2.6.24, you will need
-# to substitute "usb" with "usb_device". It shouldn't hurt to use two lines
-# (one each way) for compatibility with older systems.
-
-# HIDAPI/libusb
-SUBSYSTEM=="usb", ATTRS{idVendor}=="04d8", ATTRS{idProduct}=="003f", MODE="0666"
-
-
-# If you are using the hidraw implementation (linux/hid.c), then do something
-# like the following, substituting the VID and PID with your device. Busnum 1
-# is USB.
-
-# HIDAPI/hidraw
-KERNEL=="hidraw*", ATTRS{busnum}=="1", ATTRS{idVendor}=="04d8", ATTRS{idProduct}=="003f", MODE="0666"
-
-# Once done, optionally rename this file for your device, and drop it into
-# /etc/udev/rules.d and unplug and re-plug your device. This is all that is
-# necessary to see the new permissions. Udev does not have to be restarted.
-
-# Note that the hexadecimal values for VID and PID are case sensitive and
-# must be lower case.
-
-# If you think permissions of 0666 are too loose, then see:
-# http://reactivated.net/writing_udev_rules.html for more information on finer
-# grained permission setting. For example, it might be sufficient to just
-# set the group or user owner for specific devices (for example the plugdev
-# group on some systems).

+ 17 - 0
src/hidapi/windows/.gitignore

@@ -0,0 +1,17 @@
+Debug
+Release
+.vs/
+*.exp
+*.ilk
+*.lib
+*.suo
+*.vcproj.*
+*.vcxproj.*
+*.ncb
+*.suo
+*.dll
+*.pdb
+.deps
+.libs
+*.lo
+*.la

+ 63 - 0
src/hidapi/windows/CMakeLists.txt

@@ -0,0 +1,63 @@
+list(APPEND HIDAPI_PUBLIC_HEADERS "hidapi_winapi.h")
+
+set(SOURCES
+    hid.c
+    hidapi_cfgmgr32.h
+    hidapi_descriptor_reconstruct.c
+    hidapi_descriptor_reconstruct.h
+    hidapi_hidclass.h
+    hidapi_hidpi.h
+    hidapi_hidsdi.h
+)
+
+if(BUILD_SHARED_LIBS)
+    list(APPEND SOURCES hidapi.rc)
+endif()
+
+add_library(hidapi_winapi
+    ${HIDAPI_PUBLIC_HEADERS}
+    ${SOURCES}
+)
+target_link_libraries(hidapi_winapi
+    PUBLIC hidapi_include
+)
+
+if(NOT BUILD_SHARED_LIBS)
+    target_compile_definitions(hidapi_winapi
+        # prevent marking functions as __declspec(dllexport) for static library build
+        # #480: this should be refactored for v1.0
+        PUBLIC HID_API_NO_EXPORT_DEFINE
+    )
+endif()
+
+set_target_properties(hidapi_winapi
+    PROPERTIES
+        EXPORT_NAME "winapi"
+        OUTPUT_NAME "hidapi"
+        VERSION ${PROJECT_VERSION}
+        PUBLIC_HEADER "${HIDAPI_PUBLIC_HEADERS}"
+)
+
+# compatibility with find_package()
+add_library(hidapi::winapi ALIAS hidapi_winapi)
+# compatibility with raw library link
+add_library(hidapi ALIAS hidapi_winapi)
+
+if(HIDAPI_INSTALL_TARGETS)
+    install(TARGETS hidapi_winapi EXPORT hidapi
+        RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}"
+        LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}"
+        ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}"
+        PUBLIC_HEADER DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/hidapi"
+    )
+endif()
+
+hidapi_configure_pc("${PROJECT_ROOT}/pc/hidapi.pc.in")
+
+if(HIDAPI_WITH_TESTS)
+     add_subdirectory(test)
+endif()
+
+if(DEFINED HIDAPI_BUILD_PP_DATA_DUMP AND HIDAPI_BUILD_PP_DATA_DUMP)
+    add_subdirectory(pp_data_dump)
+endif()

+ 0 - 1
src/hidapi/windows/Makefile.am

@@ -8,7 +8,6 @@ hdrdir = $(includedir)/hidapi
 hdr_HEADERS = $(top_srcdir)/hidapi/hidapi.h
 
 EXTRA_DIST = \
-  ddk_build \
   hidapi.vcproj \
   hidtest.vcproj \
   Makefile-manual \

+ 6 - 11
src/hidapi/windows/Makefile.mingw

@@ -9,16 +9,14 @@
 all: hidtest libhidapi.dll
 
 CC=gcc
-CXX=g++
-COBJS=hid.o
-CPPOBJS=../hidtest/hidtest.o
-OBJS=$(COBJS) $(CPPOBJS)
-CFLAGS=-I../hidapi -g -c
-LIBS= -lsetupapi
-DLL_LDFLAGS = -mwindows -lsetupapi
+COBJS=hid.o ../hidtest/test.o
+OBJS=$(COBJS)
+CFLAGS=-I../hidapi -I. -g -c
+LIBS=
+DLL_LDFLAGS = -mwindows
 
 hidtest: $(OBJS)
-	g++ -g $^ $(LIBS) -o hidtest
+	$(CC) -g $^ $(LIBS) -o hidtest
 
 libhidapi.dll: $(OBJS)
 	$(CC) -g $^ $(DLL_LDFLAGS) -o libhidapi.dll
@@ -26,9 +24,6 @@ libhidapi.dll: $(OBJS)
 $(COBJS): %.o: %.c
 	$(CC) $(CFLAGS) $< -o $@
 
-$(CPPOBJS): %.o: %.cpp
-	$(CXX) $(CFLAGS) $< -o $@
-
 clean:
 	rm *.o ../hidtest/*.o hidtest.exe
 

+ 0 - 17
src/hidapi/windows/ddk_build/hidapi.def

@@ -1,17 +0,0 @@
-LIBRARY   hidapi
-EXPORTS
-   hid_open    @1
-   hid_write   @2
-   hid_read    @3
-   hid_close   @4
-   hid_get_product_string @5
-   hid_get_manufacturer_string @6
-   hid_get_serial_number_string @7
-   hid_get_indexed_string @8
-   hid_error @9
-   hid_set_nonblocking @10
-   hid_enumerate @11
-   hid_open_path @12
-   hid_send_feature_report @13
-   hid_get_feature_report @14
-   

+ 0 - 49
src/hidapi/windows/ddk_build/makefile

@@ -1,49 +0,0 @@
-#############################################################################
-#
-#               Copyright (C) Microsoft Corporation 1995, 1996
-#       All Rights Reserved.
-#
-#       MAKEFILE for HID directory
-#
-#############################################################################
-
-!IFDEF WIN95_BUILD
-
-ROOT=..\..\..\..
-
-VERSIONLIST = debug retail
-IS_32 = TRUE
-IS_SDK = TRUE
-IS_PRIVATE = TRUE
-IS_SDK = TRUE
-IS_DDK = TRUE
-WIN32 = TRUE
-COMMONMKFILE = hidapi.mk
-
-!include $(ROOT)\dev\master.mk
-
-
-!ELSE
-
-#
-# DO NOT EDIT THIS FILE!!!  Edit .\sources. if you want to add a new source
-# file to this component.  This file merely indirects to the real make file
-# that is shared by all the driver components of the Windows NT DDK
-#
-
-!IF DEFINED(_NT_TARGET_VERSION)
-!	IF $(_NT_TARGET_VERSION)>=0x501
-!		INCLUDE $(NTMAKEENV)\makefile.def
-!	ELSE
-#               Only warn once per directory
-!               INCLUDE $(NTMAKEENV)\makefile.plt
-!               IF "$(BUILD_PASS)"=="PASS1"
-!		    message BUILDMSG: Warning : The sample "$(MAKEDIR)" is not valid for the current OS target.
-!               ENDIF
-!	ENDIF
-!ELSE
-!	INCLUDE $(NTMAKEENV)\makefile.def
-!ENDIF
-
-!ENDIF
-

+ 0 - 23
src/hidapi/windows/ddk_build/sources

@@ -1,23 +0,0 @@
-TARGETNAME=hidapi
-TARGETTYPE=DYNLINK
-UMTYPE=console
-UMENTRY=main
-
-MSC_WARNING_LEVEL=/W3 /WX
-
-TARGETLIBS=$(SDK_LIB_PATH)\hid.lib \
-           $(SDK_LIB_PATH)\setupapi.lib \
-           $(SDK_LIB_PATH)\kernel32.lib \
-           $(SDK_LIB_PATH)\comdlg32.lib
-
-USE_MSVCRT=1
-
-INCLUDES= ..\..\hidapi
-SOURCES= ..\hid.c \
-
-
-TARGET_DESTINATION=retail
-
-MUI=0
-MUI_COMMENT="HID Interface DLL"
-

File diff suppressed because it is too large
+ 429 - 456
src/hidapi/windows/hid.c


+ 35 - 0
src/hidapi/windows/hidapi.rc

@@ -0,0 +1,35 @@
+#include "winresrc.h"
+
+#include "hidapi.h"
+
+// English
+LANGUAGE LANG_ENGLISH, SUBLANG_DEFAULT
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION HID_API_VERSION_MAJOR,HID_API_VERSION_MINOR,HID_API_VERSION_PATCH,0
+ PRODUCTVERSION HID_API_VERSION_MAJOR,HID_API_VERSION_MINOR,HID_API_VERSION_PATCH,0
+ FILEFLAGSMASK VS_FFI_FILEFLAGSMASK
+ FILEFLAGS 0
+#ifdef _DEBUG
+  | VS_FF_DEBUG
+#endif
+ FILEOS VOS_NT_WINDOWS32
+ FILETYPE VFT_DLL
+BEGIN
+	BLOCK "StringFileInfo"
+	BEGIN
+		BLOCK "04090000"
+		BEGIN
+			VALUE "CompanyName", "libusb/hidapi Team"
+			VALUE "FileDescription", "A multi-platform library to interface with HID devices (USB, Bluetooth, etc.)"
+			VALUE "FileVersion", HID_API_VERSION_STR
+			VALUE "ProductName", "HIDAPI"
+			VALUE "ProductVersion", HID_API_VERSION_STR
+			VALUE "Licence", "https://github.com/libusb/hidapi/blob/master/LICENSE.txt"
+			VALUE "Comments", "https://github.com/libusb/hidapi"
+		END
+	END
+	BLOCK "VarFileInfo"
+	BEGIN
+		VALUE "Translation", 0x409, 0
+	END
+END

+ 41 - 29
src/hidapi/windows/hidapi.sln

@@ -1,29 +1,41 @@
-
-Microsoft Visual Studio Solution File, Format Version 10.00
-# Visual C++ Express 2008
-Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "hidapi", "hidapi.vcproj", "{A107C21C-418A-4697-BB10-20C3AA60E2E4}"
-EndProject
-Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "hidtest", "hidtest.vcproj", "{23E9FF6A-49D1-4993-B2B5-BBB992C6C712}"
-	ProjectSection(ProjectDependencies) = postProject
-		{A107C21C-418A-4697-BB10-20C3AA60E2E4} = {A107C21C-418A-4697-BB10-20C3AA60E2E4}
-	EndProjectSection
-EndProject
-Global
-	GlobalSection(SolutionConfigurationPlatforms) = preSolution
-		Debug|Win32 = Debug|Win32
-		Release|Win32 = Release|Win32
-	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}.Release|Win32.ActiveCfg = Release|Win32
-		{A107C21C-418A-4697-BB10-20C3AA60E2E4}.Release|Win32.Build.0 = Release|Win32
-		{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}.Release|Win32.ActiveCfg = Release|Win32
-		{23E9FF6A-49D1-4993-B2B5-BBB992C6C712}.Release|Win32.Build.0 = Release|Win32
-	EndGlobalSection
-	GlobalSection(SolutionProperties) = preSolution
-		HideSolutionNode = FALSE
-	EndGlobalSection
-EndGlobal
+
+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

+ 200 - 201
src/hidapi/windows/hidapi.vcproj

@@ -1,201 +1,200 @@
-<?xml version="1.0" encoding="Windows-1252"?>
-<VisualStudioProject
-	ProjectType="Visual C++"
-	Version="9.00"
-	Name="hidapi"
-	ProjectGUID="{A107C21C-418A-4697-BB10-20C3AA60E2E4}"
-	RootNamespace="hidapi"
-	Keyword="Win32Proj"
-	TargetFrameworkVersion="196613"
-	>
-	<Platforms>
-		<Platform
-			Name="Win32"
-		/>
-	</Platforms>
-	<ToolFiles>
-	</ToolFiles>
-	<Configurations>
-		<Configuration
-			Name="Debug|Win32"
-			OutputDirectory="$(SolutionDir)$(ConfigurationName)"
-			IntermediateDirectory="$(ConfigurationName)"
-			ConfigurationType="2"
-			CharacterSet="1"
-			>
-			<Tool
-				Name="VCPreBuildEventTool"
-			/>
-			<Tool
-				Name="VCCustomBuildTool"
-			/>
-			<Tool
-				Name="VCXMLDataGeneratorTool"
-			/>
-			<Tool
-				Name="VCWebServiceProxyGeneratorTool"
-			/>
-			<Tool
-				Name="VCMIDLTool"
-			/>
-			<Tool
-				Name="VCCLCompilerTool"
-				Optimization="0"
-				AdditionalIncludeDirectories="..\hidapi"
-				PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_USRDLL;HIDAPI_EXPORTS"
-				MinimalRebuild="true"
-				BasicRuntimeChecks="3"
-				RuntimeLibrary="3"
-				UsePrecompiledHeader="0"
-				WarningLevel="3"
-				DebugInformationFormat="4"
-			/>
-			<Tool
-				Name="VCManagedResourceCompilerTool"
-			/>
-			<Tool
-				Name="VCResourceCompilerTool"
-			/>
-			<Tool
-				Name="VCPreLinkEventTool"
-			/>
-			<Tool
-				Name="VCLinkerTool"
-				AdditionalDependencies="setupapi.lib"
-				LinkIncremental="2"
-				GenerateDebugInformation="true"
-				SubSystem="2"
-				TargetMachine="1"
-			/>
-			<Tool
-				Name="VCALinkTool"
-			/>
-			<Tool
-				Name="VCManifestTool"
-			/>
-			<Tool
-				Name="VCXDCMakeTool"
-			/>
-			<Tool
-				Name="VCBscMakeTool"
-			/>
-			<Tool
-				Name="VCFxCopTool"
-			/>
-			<Tool
-				Name="VCAppVerifierTool"
-			/>
-			<Tool
-				Name="VCPostBuildEventTool"
-			/>
-		</Configuration>
-		<Configuration
-			Name="Release|Win32"
-			OutputDirectory="$(SolutionDir)$(ConfigurationName)"
-			IntermediateDirectory="$(ConfigurationName)"
-			ConfigurationType="2"
-			CharacterSet="1"
-			WholeProgramOptimization="1"
-			>
-			<Tool
-				Name="VCPreBuildEventTool"
-			/>
-			<Tool
-				Name="VCCustomBuildTool"
-			/>
-			<Tool
-				Name="VCXMLDataGeneratorTool"
-			/>
-			<Tool
-				Name="VCWebServiceProxyGeneratorTool"
-			/>
-			<Tool
-				Name="VCMIDLTool"
-			/>
-			<Tool
-				Name="VCCLCompilerTool"
-				Optimization="2"
-				EnableIntrinsicFunctions="true"
-				AdditionalIncludeDirectories="..\hidapi"
-				PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_USRDLL;HIDAPI_EXPORTS"
-				RuntimeLibrary="2"
-				EnableFunctionLevelLinking="true"
-				UsePrecompiledHeader="0"
-				WarningLevel="3"
-				DebugInformationFormat="3"
-			/>
-			<Tool
-				Name="VCManagedResourceCompilerTool"
-			/>
-			<Tool
-				Name="VCResourceCompilerTool"
-			/>
-			<Tool
-				Name="VCPreLinkEventTool"
-			/>
-			<Tool
-				Name="VCLinkerTool"
-				AdditionalDependencies="setupapi.lib"
-				LinkIncremental="1"
-				GenerateDebugInformation="true"
-				SubSystem="2"
-				OptimizeReferences="2"
-				EnableCOMDATFolding="2"
-				TargetMachine="1"
-			/>
-			<Tool
-				Name="VCALinkTool"
-			/>
-			<Tool
-				Name="VCManifestTool"
-			/>
-			<Tool
-				Name="VCXDCMakeTool"
-			/>
-			<Tool
-				Name="VCBscMakeTool"
-			/>
-			<Tool
-				Name="VCFxCopTool"
-			/>
-			<Tool
-				Name="VCAppVerifierTool"
-			/>
-			<Tool
-				Name="VCPostBuildEventTool"
-			/>
-		</Configuration>
-	</Configurations>
-	<References>
-	</References>
-	<Files>
-		<Filter
-			Name="Source Files"
-			Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx"
-			UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"
-			>
-			<File
-				RelativePath=".\hid.c"
-				>
-			</File>
-		</Filter>
-		<Filter
-			Name="Header Files"
-			Filter="h;hpp;hxx;hm;inl;inc;xsd"
-			UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"
-			>
-			<File
-				RelativePath="..\hidapi\hidapi.h"
-				>
-			</File>
-		</Filter>
-		<Filter
-			Name="Resource Files"
-			Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav"
-			UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}"
-			>
-		</Filter>
-	</Files>
-	<Globals>
-	</Globals>
-</VisualStudioProject>
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+	ProjectType="Visual C++"
+	Version="9.00"
+	Name="hidapi"
+	ProjectGUID="{A107C21C-418A-4697-BB10-20C3AA60E2E4}"
+	RootNamespace="hidapi"
+	Keyword="Win32Proj"
+	TargetFrameworkVersion="196613"
+	>
+	<Platforms>
+		<Platform
+			Name="Win32"
+		/>
+	</Platforms>
+	<ToolFiles>
+	</ToolFiles>
+	<Configurations>
+		<Configuration
+			Name="Debug|Win32"
+			OutputDirectory="$(SolutionDir)$(ConfigurationName)"
+			IntermediateDirectory="$(ConfigurationName)"
+			ConfigurationType="2"
+			CharacterSet="1"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				Optimization="0"
+				AdditionalIncludeDirectories="..\hidapi"
+				PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_USRDLL;HIDAPI_EXPORTS"
+				MinimalRebuild="true"
+				BasicRuntimeChecks="3"
+				RuntimeLibrary="3"
+				UsePrecompiledHeader="0"
+				WarningLevel="3"
+				DebugInformationFormat="4"
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLinkerTool"
+				LinkIncremental="2"
+				GenerateDebugInformation="true"
+				SubSystem="2"
+				TargetMachine="1"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCManifestTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCAppVerifierTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+		</Configuration>
+		<Configuration
+			Name="Release|Win32"
+			OutputDirectory="$(SolutionDir)$(ConfigurationName)"
+			IntermediateDirectory="$(ConfigurationName)"
+			ConfigurationType="2"
+			CharacterSet="1"
+			WholeProgramOptimization="1"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				Optimization="2"
+				EnableIntrinsicFunctions="true"
+				AdditionalIncludeDirectories="..\hidapi"
+				PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_USRDLL;HIDAPI_EXPORTS"
+				RuntimeLibrary="2"
+				EnableFunctionLevelLinking="true"
+				UsePrecompiledHeader="0"
+				WarningLevel="3"
+				DebugInformationFormat="3"
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLinkerTool"
+				AdditionalDependencies="setupapi.lib"
+				LinkIncremental="1"
+				GenerateDebugInformation="true"
+				SubSystem="2"
+				OptimizeReferences="2"
+				EnableCOMDATFolding="2"
+				TargetMachine="1"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCManifestTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCAppVerifierTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+		</Configuration>
+	</Configurations>
+	<References>
+	</References>
+	<Files>
+		<Filter
+			Name="Source Files"
+			Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx"
+			UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"
+			>
+			<File
+				RelativePath=".\hid.c"
+				>
+			</File>
+		</Filter>
+		<Filter
+			Name="Header Files"
+			Filter="h;hpp;hxx;hm;inl;inc;xsd"
+			UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"
+			>
+			<File
+				RelativePath="..\hidapi\hidapi.h"
+				>
+			</File>
+		</Filter>
+		<Filter
+			Name="Resource Files"
+			Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav"
+			UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}"
+			>
+		</Filter>
+	</Files>
+	<Globals>
+	</Globals>
+</VisualStudioProject>

+ 200 - 0
src/hidapi/windows/hidapi.vcxproj

@@ -0,0 +1,200 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <ItemGroup Label="ProjectConfigurations">
+    <ProjectConfiguration Include="Debug|Win32">
+      <Configuration>Debug</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Debug|x64">
+      <Configuration>Debug</Configuration>
+      <Platform>x64</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Release|Win32">
+      <Configuration>Release</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Release|x64">
+      <Configuration>Release</Configuration>
+      <Platform>x64</Platform>
+    </ProjectConfiguration>
+  </ItemGroup>
+  <PropertyGroup Label="Globals">
+    <ProjectGuid>{A107C21C-418A-4697-BB10-20C3AA60E2E4}</ProjectGuid>
+    <RootNamespace>hidapi</RootNamespace>
+    <Keyword>Win32Proj</Keyword>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+    <ConfigurationType>DynamicLibrary</ConfigurationType>
+    <PlatformToolset Condition="'$(VisualStudioVersion)'=='11'">v110</PlatformToolset>
+    <PlatformToolset Condition="'$(VisualStudioVersion)'=='12'">v120</PlatformToolset>
+    <PlatformToolset Condition="'$(VisualStudioVersion)'=='14'">v140</PlatformToolset>
+    <PlatformToolset Condition="'$(VisualStudioVersion)'=='15'">v141</PlatformToolset>
+    <PlatformToolset Condition="'$(VisualStudioVersion)'=='16'">v142</PlatformToolset>
+    <PlatformToolset Condition="'$(VisualStudioVersion)'=='17'">v143</PlatformToolset>
+    <CharacterSet>Unicode</CharacterSet>
+    <WholeProgramOptimization>true</WholeProgramOptimization>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+    <ConfigurationType>DynamicLibrary</ConfigurationType>
+    <PlatformToolset Condition="'$(VisualStudioVersion)'=='11'">v110</PlatformToolset>
+    <PlatformToolset Condition="'$(VisualStudioVersion)'=='12'">v120</PlatformToolset>
+    <PlatformToolset Condition="'$(VisualStudioVersion)'=='14'">v140</PlatformToolset>
+    <PlatformToolset Condition="'$(VisualStudioVersion)'=='15'">v141</PlatformToolset>
+    <PlatformToolset Condition="'$(VisualStudioVersion)'=='16'">v142</PlatformToolset>
+    <PlatformToolset Condition="'$(VisualStudioVersion)'=='17'">v143</PlatformToolset>
+    <CharacterSet>Unicode</CharacterSet>
+    <WholeProgramOptimization>true</WholeProgramOptimization>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+    <ConfigurationType>DynamicLibrary</ConfigurationType>
+    <PlatformToolset Condition="'$(VisualStudioVersion)'=='11'">v110</PlatformToolset>
+    <PlatformToolset Condition="'$(VisualStudioVersion)'=='12'">v120</PlatformToolset>
+    <PlatformToolset Condition="'$(VisualStudioVersion)'=='14'">v140</PlatformToolset>
+    <PlatformToolset Condition="'$(VisualStudioVersion)'=='15'">v141</PlatformToolset>
+    <PlatformToolset Condition="'$(VisualStudioVersion)'=='16'">v142</PlatformToolset>
+    <PlatformToolset Condition="'$(VisualStudioVersion)'=='17'">v143</PlatformToolset>
+    <CharacterSet>Unicode</CharacterSet>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+    <ConfigurationType>DynamicLibrary</ConfigurationType>
+    <PlatformToolset Condition="'$(VisualStudioVersion)'=='11'">v110</PlatformToolset>
+    <PlatformToolset Condition="'$(VisualStudioVersion)'=='12'">v120</PlatformToolset>
+    <PlatformToolset Condition="'$(VisualStudioVersion)'=='14'">v140</PlatformToolset>
+    <PlatformToolset Condition="'$(VisualStudioVersion)'=='15'">v141</PlatformToolset>
+    <PlatformToolset Condition="'$(VisualStudioVersion)'=='16'">v142</PlatformToolset>
+    <PlatformToolset Condition="'$(VisualStudioVersion)'=='17'">v143</PlatformToolset>
+    <CharacterSet>Unicode</CharacterSet>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+  <ImportGroup Label="ExtensionSettings">
+  </ImportGroup>
+  <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <PropertyGroup Label="UserMacros" />
+  <PropertyGroup>
+    <_ProjectFileVersion>14.0.25431.1</_ProjectFileVersion>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" />
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" />
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" />
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" />
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+    <ClCompile>
+      <Optimization>Disabled</Optimization>
+      <AdditionalIncludeDirectories>..\hidapi;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;_USRDLL;HIDAPI_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <MinimalRebuild>true</MinimalRebuild>
+      <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
+      <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
+      <PrecompiledHeader />
+      <WarningLevel>Level3</WarningLevel>
+      <DebugInformationFormat>EditAndContinue</DebugInformationFormat>
+    </ClCompile>
+    <Link>
+      <AdditionalDependencies>%(AdditionalDependencies)</AdditionalDependencies>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+      <SubSystem>Windows</SubSystem>
+      <TargetMachine>MachineX86</TargetMachine>
+    </Link>
+    <ResourceCompile>
+      <AdditionalIncludeDirectories>..\hidapi</AdditionalIncludeDirectories>
+      <PreprocessorDefinitions>_DEBUG</PreprocessorDefinitions>
+    </ResourceCompile>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+    <ClCompile>
+      <Optimization>Disabled</Optimization>
+      <AdditionalIncludeDirectories>..\hidapi;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;_USRDLL;HIDAPI_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
+      <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
+      <PrecompiledHeader>
+      </PrecompiledHeader>
+      <WarningLevel>Level3</WarningLevel>
+      <DebugInformationFormat>EditAndContinue</DebugInformationFormat>
+    </ClCompile>
+    <Link>
+      <AdditionalDependencies>%(AdditionalDependencies)</AdditionalDependencies>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+      <SubSystem>Windows</SubSystem>
+    </Link>
+    <ResourceCompile>
+      <AdditionalIncludeDirectories>..\hidapi</AdditionalIncludeDirectories>
+      <PreprocessorDefinitions>_DEBUG</PreprocessorDefinitions>
+    </ResourceCompile>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+    <ClCompile>
+      <Optimization>MaxSpeed</Optimization>
+      <IntrinsicFunctions>true</IntrinsicFunctions>
+      <AdditionalIncludeDirectories>..\hidapi;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;_USRDLL;HIDAPI_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+      <FunctionLevelLinking>true</FunctionLevelLinking>
+      <PrecompiledHeader />
+      <WarningLevel>Level3</WarningLevel>
+      <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+    </ClCompile>
+    <Link>
+      <AdditionalDependencies>%(AdditionalDependencies)</AdditionalDependencies>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+      <SubSystem>Windows</SubSystem>
+      <OptimizeReferences>true</OptimizeReferences>
+      <EnableCOMDATFolding>true</EnableCOMDATFolding>
+      <TargetMachine>MachineX86</TargetMachine>
+    </Link>
+    <ResourceCompile>
+      <AdditionalIncludeDirectories>..\hidapi</AdditionalIncludeDirectories>
+      <PreprocessorDefinitions>NDEBUG</PreprocessorDefinitions>
+    </ResourceCompile>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+    <ClCompile>
+      <Optimization>MaxSpeed</Optimization>
+      <IntrinsicFunctions>true</IntrinsicFunctions>
+      <AdditionalIncludeDirectories>..\hidapi;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;_USRDLL;HIDAPI_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+      <FunctionLevelLinking>true</FunctionLevelLinking>
+      <PrecompiledHeader>
+      </PrecompiledHeader>
+      <WarningLevel>Level3</WarningLevel>
+      <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+    </ClCompile>
+    <Link>
+      <AdditionalDependencies>%(AdditionalDependencies)</AdditionalDependencies>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+      <SubSystem>Windows</SubSystem>
+      <OptimizeReferences>true</OptimizeReferences>
+      <EnableCOMDATFolding>true</EnableCOMDATFolding>
+    </Link>
+    <ResourceCompile>
+      <AdditionalIncludeDirectories>..\hidapi</AdditionalIncludeDirectories>
+      <PreprocessorDefinitions>NDEBUG</PreprocessorDefinitions>
+    </ResourceCompile>
+  </ItemDefinitionGroup>
+  <ItemGroup>
+    <ClCompile Include="hid.c" />
+  </ItemGroup>
+  <ItemGroup>
+    <ClInclude Include="..\hidapi\hidapi.h" />
+    <ClInclude Include="hidapi_winapi.h" />
+  </ItemGroup>
+  <ItemGroup>
+    <ResourceCompile Include="hidapi.rc" />
+  </ItemGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+  <ImportGroup Label="ExtensionTargets">
+  </ImportGroup>
+</Project>

+ 14 - 16
src/hidapi/windows/hidapi_cfgmgr32.h

@@ -28,18 +28,14 @@
 #include <propkey.h>
 
 #else
-#ifndef PROPERTYKEY_DEFINED
-#define PROPERTYKEY_DEFINED
-typedef struct
-{
-    GUID fmtid;
-    DWORD pid;
-} PROPERTYKEY;
-#endif /* PROPERTYKEY_DEFINED */
 
 /* This part of the header mimics cfgmgr32.h,
     but only what is used by HIDAPI */
 
+#include <initguid.h>
+#include <devpropdef.h>
+#include <propkeydef.h>
+
 typedef DWORD RETURN_TYPE;
 typedef RETURN_TYPE CONFIGRET;
 typedef DWORD DEVNODE, DEVINST;
@@ -59,18 +55,20 @@ typedef CONFIGRET(__stdcall* CM_Get_Parent_)(PDEVINST pdnDevInst, DEVINST dnDevI
 typedef CONFIGRET(__stdcall* CM_Get_DevNode_PropertyW_)(DEVINST dnDevInst, CONST DEVPROPKEY* PropertyKey, DEVPROPTYPE* PropertyType, PBYTE PropertyBuffer, PULONG PropertyBufferSize, ULONG ulFlags);
 typedef CONFIGRET(__stdcall* CM_Get_Device_Interface_PropertyW_)(LPCWSTR pszDeviceInterface, CONST DEVPROPKEY* PropertyKey, DEVPROPTYPE* PropertyType, PBYTE PropertyBuffer, PULONG PropertyBufferSize, ULONG ulFlags);
 typedef CONFIGRET(__stdcall* CM_Get_Device_Interface_List_SizeW_)(PULONG pulLen, LPGUID InterfaceClassGuid, DEVINSTID_W pDeviceID, ULONG ulFlags);
-typedef CONFIGRET(__stdcall* CM_Get_Device_Interface_ListW_)(LPGUID InterfaceClassGuid, DEVINSTID_W pDeviceID, WCHAR* Buffer, ULONG BufferLen, ULONG ulFlags);
+typedef CONFIGRET(__stdcall* CM_Get_Device_Interface_ListW_)(LPGUID InterfaceClassGuid, DEVINSTID_W pDeviceID, PZZWSTR Buffer, ULONG BufferLen, ULONG ulFlags);
 
 // from devpkey.h
-static DEVPROPKEY DEVPKEY_NAME = { { 0xb725f130, 0x47ef, 0x101a, {0xa5, 0xf1, 0x02, 0x60, 0x8c, 0x9e, 0xeb, 0xac} }, 10 }; // DEVPROP_TYPE_STRING
-static DEVPROPKEY DEVPKEY_Device_InstanceId = { { 0x78c34fc8, 0x104a, 0x4aca, {0x9e, 0xa4, 0x52, 0x4d, 0x52, 0x99, 0x6e, 0x57} }, 256 }; // DEVPROP_TYPE_STRING
-//static DEVPROPKEY DEVPKEY_Device_HardwareIds = { { 0xa45c254e, 0xdf1c, 0x4efd, {0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0} }, 3 }; // DEVPROP_TYPE_STRING_LIST
-static DEVPROPKEY DEVPKEY_Device_CompatibleIds = { { 0xa45c254e, 0xdf1c, 0x4efd, {0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0} }, 4 }; // DEVPROP_TYPE_STRING_LIST
-//static DEVPROPKEY DEVPKEY_Device_ContainerId = { { 0x8c7ed206, 0x3f8a, 0x4827, {0xb3, 0xab, 0xae, 0x9e, 0x1f, 0xae, 0xfc, 0x6c} }, 2 }; // DEVPROP_TYPE_GUID
+DEFINE_DEVPROPKEY(DEVPKEY_NAME, 0xb725f130, 0x47ef, 0x101a, 0xa5, 0xf1, 0x02, 0x60, 0x8c, 0x9e, 0xeb, 0xac, 10); // DEVPROP_TYPE_STRING
+DEFINE_DEVPROPKEY(DEVPKEY_Device_Manufacturer, 0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0, 13); // DEVPROP_TYPE_STRING
+DEFINE_DEVPROPKEY(DEVPKEY_Device_InstanceId, 0x78c34fc8, 0x104a, 0x4aca, 0x9e, 0xa4, 0x52, 0x4d, 0x52, 0x99, 0x6e, 0x57, 256); // DEVPROP_TYPE_STRING
+DEFINE_DEVPROPKEY(DEVPKEY_Device_HardwareIds, 0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0, 3); // DEVPROP_TYPE_STRING_LIST
+DEFINE_DEVPROPKEY(DEVPKEY_Device_CompatibleIds, 0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0, 4); // DEVPROP_TYPE_STRING_LIST
+DEFINE_DEVPROPKEY(DEVPKEY_Device_ContainerId, 0x8c7ed206, 0x3f8a, 0x4827, 0xb3, 0xab, 0xae, 0x9e, 0x1f, 0xae, 0xfc, 0x6c, 2); // DEVPROP_TYPE_GUID
 
 // from propkey.h
-static PROPERTYKEY PKEY_DeviceInterface_Bluetooth_DeviceAddress = { { 0x2bd67d8b, 0x8beb, 0x48d5, {0x87, 0xe0, 0x6c, 0xda, 0x34, 0x28, 0x04, 0x0a} }, 1 }; // DEVPROP_TYPE_STRING
-static PROPERTYKEY PKEY_DeviceInterface_Bluetooth_Manufacturer = { { 0x2bd67d8b, 0x8beb, 0x48d5, {0x87, 0xe0, 0x6c, 0xda, 0x34, 0x28, 0x04, 0x0a} }, 4 }; // DEVPROP_TYPE_STRING
+DEFINE_PROPERTYKEY(PKEY_DeviceInterface_Bluetooth_DeviceAddress, 0x2BD67D8B, 0x8BEB, 0x48D5, 0x87, 0xE0, 0x6C, 0xDA, 0x34, 0x28, 0x04, 0x0A, 1); // DEVPROP_TYPE_STRING
+DEFINE_PROPERTYKEY(PKEY_DeviceInterface_Bluetooth_Manufacturer, 0x2BD67D8B, 0x8BEB, 0x48D5, 0x87, 0xE0, 0x6C, 0xDA, 0x34, 0x28, 0x04, 0x0A, 4); // DEVPROP_TYPE_STRING
+DEFINE_PROPERTYKEY(PKEY_DeviceInterface_Bluetooth_ModelNumber, 0x2BD67D8B, 0x8BEB, 0x48D5, 0x87, 0xE0, 0x6C, 0xDA, 0x34, 0x28, 0x04, 0x0A, 5); // DEVPROP_TYPE_STRING
 
 #endif
 

+ 987 - 0
src/hidapi/windows/hidapi_descriptor_reconstruct.c

@@ -0,0 +1,987 @@
+/*******************************************************
+ HIDAPI - Multi-Platform library for
+ communication with HID devices.
+
+ libusb/hidapi Team
+
+ Copyright 2022, All Rights Reserved.
+
+ At the discretion of the user of this library,
+ this software may be licensed under the terms of the
+ GNU General Public License v3, a BSD-Style license, or the
+ original HIDAPI license as outlined in the LICENSE.txt,
+ LICENSE-gpl3.txt, LICENSE-bsd.txt, and LICENSE-orig.txt
+ files located at the root of the source distribution.
+ These files may also be found in the public source
+ code repository located at:
+        https://github.com/libusb/hidapi .
+********************************************************/
+#include "hidapi_descriptor_reconstruct.h"
+
+/**
+ * @brief References to report descriptor buffer.
+ * 
+ */
+struct rd_buffer {
+	unsigned char* buf; /* Pointer to the array which stores the reconstructed descriptor */
+	size_t buf_size; /* Size of the buffer in bytes */
+	size_t byte_idx; /* Index of the next report byte to write to buf array */
+};
+
+/**
+ * @brief Function that appends a byte to encoded report descriptor buffer.
+ *
+ * @param[in]  byte     Single byte to append.
+ * @param      rpt_desc Pointer to report descriptor buffer struct.
+ */
+static void rd_append_byte(unsigned char byte, struct rd_buffer* rpt_desc) {
+	if (rpt_desc->byte_idx < rpt_desc->buf_size) {
+		rpt_desc->buf[rpt_desc->byte_idx] = byte;
+		rpt_desc->byte_idx++;
+	}
+}
+
+/**
+ * @brief Writes a short report descriptor item according USB HID spec 1.11 chapter 6.2.2.2.
+ *
+ * @param[in]  rd_item  Enumeration identifying type (Main, Global, Local) and function (e.g Usage or Report Count) of the item.
+ * @param[in]  data     Data (Size depends on rd_item 0,1,2 or 4bytes).
+ * @param      list     Chained list of report descriptor bytes.
+ *
+ * @return Returns 0 if successful, -1 for error.
+ */
+static int rd_write_short_item(rd_items rd_item, LONG64 data, struct rd_buffer* rpt_desc) {
+	if (rd_item & 0x03) {
+		// Invalid input data, last to bits are reserved for data size
+		return -1;
+	}
+
+	if (rd_item == rd_main_collection_end) {
+		// Item without data (1Byte prefix only)
+		unsigned char oneBytePrefix = (unsigned char) rd_item + 0x00;
+		rd_append_byte(oneBytePrefix, rpt_desc);
+	}
+	else if ((rd_item == rd_global_logical_minimum) ||
+		(rd_item == rd_global_logical_maximum) ||
+		(rd_item == rd_global_physical_minimum) ||
+		(rd_item == rd_global_physical_maximum)) {
+		// Item with signed integer data
+		if ((data >= -128) && (data <= 127)) {
+			// 1Byte prefix + 1Byte data
+			unsigned char oneBytePrefix = (unsigned char) rd_item + 0x01;
+			char localData = (char)data;
+			rd_append_byte(oneBytePrefix, rpt_desc);
+			rd_append_byte(localData & 0xFF, rpt_desc);
+		}
+		else if ((data >= -32768) && (data <= 32767)) {
+			// 1Byte prefix + 2Byte data
+			unsigned char oneBytePrefix = (unsigned char) rd_item + 0x02;
+			INT16 localData = (INT16)data;
+			rd_append_byte(oneBytePrefix, rpt_desc);
+			rd_append_byte(localData & 0xFF, rpt_desc);
+			rd_append_byte(localData >> 8 & 0xFF, rpt_desc);
+		}
+		else if ((data >= -2147483648LL) && (data <= 2147483647)) {
+			// 1Byte prefix + 4Byte data
+			unsigned char oneBytePrefix = (unsigned char) rd_item + 0x03;
+			INT32 localData = (INT32)data;
+			rd_append_byte(oneBytePrefix, rpt_desc);
+			rd_append_byte(localData & 0xFF, rpt_desc);
+			rd_append_byte(localData >> 8 & 0xFF, rpt_desc);
+			rd_append_byte(localData >> 16 & 0xFF, rpt_desc);
+			rd_append_byte(localData >> 24 & 0xFF, rpt_desc);
+		}
+		else {
+			// Data out of 32 bit signed integer range
+			return -1;
+		}
+	}
+	else {
+		// Item with unsigned integer data
+		if ((data >= 0) && (data <= 0xFF)) {
+			// 1Byte prefix + 1Byte data
+			unsigned char oneBytePrefix = (unsigned char) rd_item + 0x01;
+			unsigned char localData = (unsigned char)data;
+			rd_append_byte(oneBytePrefix, rpt_desc);
+			rd_append_byte(localData & 0xFF, rpt_desc);
+		}
+		else if ((data >= 0) && (data <= 0xFFFF)) {
+			// 1Byte prefix + 2Byte data
+			unsigned char oneBytePrefix = (unsigned char) rd_item + 0x02;
+			UINT16 localData = (UINT16)data;
+			rd_append_byte(oneBytePrefix, rpt_desc);
+			rd_append_byte(localData & 0xFF, rpt_desc);
+			rd_append_byte(localData >> 8 & 0xFF, rpt_desc);
+		}
+		else if ((data >= 0) && (data <= 0xFFFFFFFF)) {
+			// 1Byte prefix + 4Byte data
+			unsigned char oneBytePrefix = (unsigned char) rd_item + 0x03;
+			UINT32 localData = (UINT32)data;
+			rd_append_byte(oneBytePrefix, rpt_desc);
+			rd_append_byte(localData & 0xFF, rpt_desc);
+			rd_append_byte(localData >> 8 & 0xFF, rpt_desc);
+			rd_append_byte(localData >> 16 & 0xFF, rpt_desc);
+			rd_append_byte(localData >> 24 & 0xFF, rpt_desc);
+		}
+		else {
+			// Data out of 32 bit unsigned integer range
+			return -1;
+		}
+	}
+	return 0;
+}
+
+static struct rd_main_item_node * rd_append_main_item_node(int first_bit, int last_bit, rd_node_type type_of_node, int caps_index, int collection_index, rd_main_items main_item_type, unsigned char report_id, struct rd_main_item_node **list) {
+	struct rd_main_item_node *new_list_node;
+
+	// Determine last node in the list
+	while (*list != NULL)
+	{
+		list = &(*list)->next;
+	}
+
+	new_list_node = malloc(sizeof(*new_list_node)); // Create new list entry
+	new_list_node->FirstBit = first_bit;
+	new_list_node->LastBit = last_bit;
+	new_list_node->TypeOfNode = type_of_node;
+	new_list_node->CapsIndex = caps_index;
+	new_list_node->CollectionIndex = collection_index;
+	new_list_node->MainItemType = main_item_type;
+	new_list_node->ReportID = report_id;
+	new_list_node->next = NULL; // NULL marks last node in the list
+
+	*list = new_list_node;
+	return new_list_node;
+}
+
+static struct  rd_main_item_node * rd_insert_main_item_node(int first_bit, int last_bit, rd_node_type type_of_node, int caps_index, int collection_index, rd_main_items main_item_type, unsigned char report_id, struct rd_main_item_node **list) {
+	// Insert item after the main item node referenced by list
+	struct rd_main_item_node *next_item = (*list)->next;
+	(*list)->next = NULL;
+	rd_append_main_item_node(first_bit, last_bit, type_of_node, caps_index, collection_index, main_item_type, report_id, list);
+	(*list)->next->next = next_item;
+	return (*list)->next;
+}
+
+static struct rd_main_item_node * rd_search_main_item_list_for_bit_position(int search_bit, rd_main_items main_item_type, unsigned char report_id, struct rd_main_item_node **list) {
+	// Determine first INPUT/OUTPUT/FEATURE main item, where the last bit position is equal or greater than the search bit position
+
+	while (((*list)->next->MainItemType != rd_collection) &&
+		((*list)->next->MainItemType != rd_collection_end) &&
+		!(((*list)->next->LastBit >= search_bit) &&
+			((*list)->next->ReportID == report_id) &&
+			((*list)->next->MainItemType == main_item_type))
+		)
+	{
+		list = &(*list)->next;
+	}
+	return *list;
+}
+
+int hid_winapi_descriptor_reconstruct_pp_data(void *preparsed_data, unsigned char *buf, size_t buf_size)
+{
+	hidp_preparsed_data *pp_data = (hidp_preparsed_data *) preparsed_data;
+
+	// Check if MagicKey is correct, to ensure that pp_data points to an valid preparse data structure
+	if (memcmp(pp_data->MagicKey, "HidP KDR", 8) != 0) {
+		return -1;
+	}
+
+	struct rd_buffer rpt_desc = {
+		.buf = buf,
+		.buf_size = buf_size,
+		.byte_idx = 0
+	};
+
+	// Set pointer to the first node of link_collection_nodes
+	phid_pp_link_collection_node link_collection_nodes = (phid_pp_link_collection_node)(((unsigned char*)&pp_data->caps[0]) + pp_data->FirstByteOfLinkCollectionArray);
+
+	// ****************************************************************************************************************************
+	// Create lookup tables for the bit range of each report per collection (position of first bit and last bit in each collection)
+	// coll_bit_range[COLLECTION_INDEX][REPORT_ID][INPUT/OUTPUT/FEATURE]
+	// ****************************************************************************************************************************
+	
+	// Allocate memory and initialize lookup table
+	rd_bit_range ****coll_bit_range;
+	coll_bit_range = malloc(pp_data->NumberLinkCollectionNodes * sizeof(*coll_bit_range));
+	for (USHORT collection_node_idx = 0; collection_node_idx < pp_data->NumberLinkCollectionNodes; collection_node_idx++) {
+		coll_bit_range[collection_node_idx] = malloc(256 * sizeof(*coll_bit_range[0])); // 256 possible report IDs (incl. 0x00)
+		for (int reportid_idx = 0; reportid_idx < 256; reportid_idx++) {
+			coll_bit_range[collection_node_idx][reportid_idx] = malloc(NUM_OF_HIDP_REPORT_TYPES * sizeof(*coll_bit_range[0][0]));
+			for (HIDP_REPORT_TYPE rt_idx = 0; rt_idx < NUM_OF_HIDP_REPORT_TYPES; rt_idx++) {
+				coll_bit_range[collection_node_idx][reportid_idx][rt_idx] = malloc(sizeof(rd_bit_range));
+				coll_bit_range[collection_node_idx][reportid_idx][rt_idx]->FirstBit = -1;
+				coll_bit_range[collection_node_idx][reportid_idx][rt_idx]->LastBit = -1;
+			}
+		}
+	}
+
+	// Fill the lookup table where caps exist
+	for (HIDP_REPORT_TYPE rt_idx = 0; rt_idx < NUM_OF_HIDP_REPORT_TYPES; rt_idx++) {
+		for (USHORT caps_idx = pp_data->caps_info[rt_idx].FirstCap; caps_idx < pp_data->caps_info[rt_idx].LastCap; caps_idx++) {
+			int first_bit, last_bit;
+			first_bit = (pp_data->caps[caps_idx].BytePosition - 1) * 8
+			           + pp_data->caps[caps_idx].BitPosition;
+			last_bit = first_bit + pp_data->caps[caps_idx].ReportSize
+			                     * pp_data->caps[caps_idx].ReportCount - 1;
+			if (coll_bit_range[pp_data->caps[caps_idx].LinkCollection][pp_data->caps[caps_idx].ReportID][rt_idx]->FirstBit == -1 ||
+				coll_bit_range[pp_data->caps[caps_idx].LinkCollection][pp_data->caps[caps_idx].ReportID][rt_idx]->FirstBit > first_bit) {
+				coll_bit_range[pp_data->caps[caps_idx].LinkCollection][pp_data->caps[caps_idx].ReportID][rt_idx]->FirstBit = first_bit;
+			}
+			if (coll_bit_range[pp_data->caps[caps_idx].LinkCollection][pp_data->caps[caps_idx].ReportID][rt_idx]->LastBit < last_bit) {
+				coll_bit_range[pp_data->caps[caps_idx].LinkCollection][pp_data->caps[caps_idx].ReportID][rt_idx]->LastBit = last_bit;
+			}
+		}
+	}
+
+	// *************************************************************************
+	// -Determine hierachy levels of each collections and store it in:
+	//  coll_levels[COLLECTION_INDEX]
+	// -Determine number of direct childs of each collections and store it in:
+	//  coll_number_of_direct_childs[COLLECTION_INDEX]
+	// *************************************************************************
+	int max_coll_level = 0;
+	int *coll_levels = malloc(pp_data->NumberLinkCollectionNodes * sizeof(coll_levels[0]));
+	int *coll_number_of_direct_childs = malloc(pp_data->NumberLinkCollectionNodes * sizeof(coll_number_of_direct_childs[0]));
+	for (USHORT collection_node_idx = 0; collection_node_idx < pp_data->NumberLinkCollectionNodes; collection_node_idx++) {
+		coll_levels[collection_node_idx] = -1;
+		coll_number_of_direct_childs[collection_node_idx] = 0;
+	}
+
+	{
+		int actual_coll_level = 0;
+		USHORT collection_node_idx = 0;
+		while (actual_coll_level >= 0) {
+			coll_levels[collection_node_idx] = actual_coll_level;
+			if ((link_collection_nodes[collection_node_idx].NumberOfChildren > 0) &&
+				(coll_levels[link_collection_nodes[collection_node_idx].FirstChild] == -1)) {
+				actual_coll_level++;
+				coll_levels[collection_node_idx] = actual_coll_level;
+				if (max_coll_level < actual_coll_level) {
+					max_coll_level = actual_coll_level;
+				}
+				coll_number_of_direct_childs[collection_node_idx]++;
+				collection_node_idx = link_collection_nodes[collection_node_idx].FirstChild;
+			}
+			else if (link_collection_nodes[collection_node_idx].NextSibling != 0) {
+				coll_number_of_direct_childs[link_collection_nodes[collection_node_idx].Parent]++;
+				collection_node_idx = link_collection_nodes[collection_node_idx].NextSibling;
+			}
+			else {
+				actual_coll_level--;
+				if (actual_coll_level >= 0) {
+					collection_node_idx = link_collection_nodes[collection_node_idx].Parent;
+				}
+			}
+		}
+	}
+
+	// *********************************************************************************
+	// Propagate the bit range of each report from the child collections to their parent
+	// and store the merged result for the parent
+	// *********************************************************************************
+	for (int actual_coll_level = max_coll_level - 1; actual_coll_level >= 0; actual_coll_level--) {
+		for (USHORT collection_node_idx = 0; collection_node_idx < pp_data->NumberLinkCollectionNodes; collection_node_idx++) {
+			if (coll_levels[collection_node_idx] == actual_coll_level) {
+				USHORT child_idx = link_collection_nodes[collection_node_idx].FirstChild;
+				while (child_idx) {
+					for (int reportid_idx = 0; reportid_idx < 256; reportid_idx++) {
+						for (HIDP_REPORT_TYPE rt_idx = 0; rt_idx < NUM_OF_HIDP_REPORT_TYPES; rt_idx++) {
+							// Merge bit range from childs
+							if ((coll_bit_range[child_idx][reportid_idx][rt_idx]->FirstBit != -1) &&
+								(coll_bit_range[collection_node_idx][reportid_idx][rt_idx]->FirstBit > coll_bit_range[child_idx][reportid_idx][rt_idx]->FirstBit)) {
+								coll_bit_range[collection_node_idx][reportid_idx][rt_idx]->FirstBit = coll_bit_range[child_idx][reportid_idx][rt_idx]->FirstBit;
+							}
+							if (coll_bit_range[collection_node_idx][reportid_idx][rt_idx]->LastBit < coll_bit_range[child_idx][reportid_idx][rt_idx]->LastBit) {
+								coll_bit_range[collection_node_idx][reportid_idx][rt_idx]->LastBit = coll_bit_range[child_idx][reportid_idx][rt_idx]->LastBit;
+							}
+							child_idx = link_collection_nodes[child_idx].NextSibling;
+						}
+					}
+				}
+			}
+		}
+	}
+
+	// *************************************************************************************************
+	// Determine child collection order of the whole hierachy, based on previously determined bit ranges
+	// and store it this index coll_child_order[COLLECTION_INDEX][DIRECT_CHILD_INDEX]
+	// *************************************************************************************************
+	USHORT **coll_child_order;
+	coll_child_order = malloc(pp_data->NumberLinkCollectionNodes * sizeof(*coll_child_order));
+	{
+		BOOLEAN *coll_parsed_flag;
+		coll_parsed_flag = malloc(pp_data->NumberLinkCollectionNodes * sizeof(coll_parsed_flag[0]));
+		for (USHORT collection_node_idx = 0; collection_node_idx < pp_data->NumberLinkCollectionNodes; collection_node_idx++) {
+			coll_parsed_flag[collection_node_idx] = FALSE;
+		}
+		int actual_coll_level = 0;
+		USHORT collection_node_idx = 0;
+		while (actual_coll_level >= 0) {
+			if ((coll_number_of_direct_childs[collection_node_idx] != 0) &&
+				(coll_parsed_flag[link_collection_nodes[collection_node_idx].FirstChild] == FALSE)) {
+				coll_parsed_flag[link_collection_nodes[collection_node_idx].FirstChild] = TRUE;
+				coll_child_order[collection_node_idx] = malloc((coll_number_of_direct_childs[collection_node_idx]) * sizeof(*coll_child_order[0]));
+
+				{
+					// Create list of child collection indices
+					// sorted reverse to the order returned to HidP_GetLinkCollectionNodeschild
+					// which seems to match teh original order, as long as no bit position needs to be considered
+					USHORT child_idx = link_collection_nodes[collection_node_idx].FirstChild;
+					int child_count = coll_number_of_direct_childs[collection_node_idx] - 1;
+					coll_child_order[collection_node_idx][child_count] = child_idx;
+					while (link_collection_nodes[child_idx].NextSibling) {
+						child_count--;
+						child_idx = link_collection_nodes[child_idx].NextSibling;
+						coll_child_order[collection_node_idx][child_count] = child_idx;
+					}
+				}
+
+				if (coll_number_of_direct_childs[collection_node_idx] > 1) {
+					// Sort child collections indices by bit positions
+					for (HIDP_REPORT_TYPE rt_idx = 0; rt_idx < NUM_OF_HIDP_REPORT_TYPES; rt_idx++) {
+						for (int reportid_idx = 0; reportid_idx < 256; reportid_idx++) {
+							for (int child_idx = 1; child_idx < coll_number_of_direct_childs[collection_node_idx]; child_idx++) {
+								// since the coll_bit_range array is not sorted, we need to reference the collection index in 
+								// our sorted coll_child_order array, and look up the corresponding bit ranges for comparing values to sort
+								int prev_coll_idx = coll_child_order[collection_node_idx][child_idx - 1];
+								int cur_coll_idx = coll_child_order[collection_node_idx][child_idx];
+								if ((coll_bit_range[prev_coll_idx][reportid_idx][rt_idx]->FirstBit != -1) &&
+									(coll_bit_range[cur_coll_idx][reportid_idx][rt_idx]->FirstBit != -1) &&
+									(coll_bit_range[prev_coll_idx][reportid_idx][rt_idx]->FirstBit > coll_bit_range[cur_coll_idx][reportid_idx][rt_idx]->FirstBit)) {
+									// Swap position indices of the two compared child collections
+									USHORT idx_latch = coll_child_order[collection_node_idx][child_idx - 1];
+									coll_child_order[collection_node_idx][child_idx - 1] = coll_child_order[collection_node_idx][child_idx];
+									coll_child_order[collection_node_idx][child_idx] = idx_latch;
+								}
+							}
+						}
+					}
+				}
+				actual_coll_level++;
+				collection_node_idx = link_collection_nodes[collection_node_idx].FirstChild;
+			}
+			else if (link_collection_nodes[collection_node_idx].NextSibling != 0) {
+				collection_node_idx = link_collection_nodes[collection_node_idx].NextSibling;
+			}
+			else {
+				actual_coll_level--;
+				if (actual_coll_level >= 0) {
+					collection_node_idx = link_collection_nodes[collection_node_idx].Parent;
+				}
+			}
+		}
+		free(coll_parsed_flag);
+	}
+
+
+	// ***************************************************************************************
+	// Create sorted main_item_list containing all the Collection and CollectionEnd main items
+	// ***************************************************************************************
+	struct rd_main_item_node *main_item_list = NULL; // List root
+	// Lookup table to find the Collection items in the list by index
+	struct rd_main_item_node **coll_begin_lookup = malloc(pp_data->NumberLinkCollectionNodes * sizeof(*coll_begin_lookup));
+	struct rd_main_item_node **coll_end_lookup = malloc(pp_data->NumberLinkCollectionNodes * sizeof(*coll_end_lookup));
+	{
+		int *coll_last_written_child = malloc(pp_data->NumberLinkCollectionNodes * sizeof(coll_last_written_child[0]));
+		for (USHORT collection_node_idx = 0; collection_node_idx < pp_data->NumberLinkCollectionNodes; collection_node_idx++) {
+			coll_last_written_child[collection_node_idx] = -1;
+		}
+
+		int actual_coll_level = 0;
+		USHORT collection_node_idx = 0;
+		struct rd_main_item_node *firstDelimiterNode = NULL;
+		struct rd_main_item_node *delimiterCloseNode = NULL;
+		coll_begin_lookup[0] = rd_append_main_item_node(0, 0, rd_item_node_collection, 0, collection_node_idx, rd_collection, 0, &main_item_list);
+		while (actual_coll_level >= 0) {
+			if ((coll_number_of_direct_childs[collection_node_idx] != 0) &&
+				(coll_last_written_child[collection_node_idx] == -1)) {
+				// Collection has child collections, but none is written to the list yet
+
+				coll_last_written_child[collection_node_idx] = coll_child_order[collection_node_idx][0];
+				collection_node_idx = coll_child_order[collection_node_idx][0];
+
+				// In a HID Report Descriptor, the first usage declared is the most preferred usage for the control.
+				// While the order in the WIN32 capabiliy strutures is the opposite:
+				// Here the preferred usage is the last aliased usage in the sequence.
+
+				if (link_collection_nodes[collection_node_idx].IsAlias && (firstDelimiterNode == NULL)) {
+					// Alliased Collection (First node in link_collection_nodes -> Last entry in report descriptor output)
+					firstDelimiterNode = main_item_list;
+					coll_begin_lookup[collection_node_idx] = rd_append_main_item_node(0, 0, rd_item_node_collection, 0, collection_node_idx, rd_delimiter_usage, 0, &main_item_list);
+					coll_begin_lookup[collection_node_idx] = rd_append_main_item_node(0, 0, rd_item_node_collection, 0, collection_node_idx, rd_delimiter_close, 0, &main_item_list);
+					delimiterCloseNode = main_item_list;
+				}
+				else {
+					// Normal not aliased collection
+					coll_begin_lookup[collection_node_idx] = rd_append_main_item_node(0, 0, rd_item_node_collection, 0, collection_node_idx, rd_collection, 0, &main_item_list);
+					actual_coll_level++;
+				}
+
+
+			}
+			else if ((coll_number_of_direct_childs[collection_node_idx] > 1) &&
+				(coll_last_written_child[collection_node_idx] != coll_child_order[collection_node_idx][coll_number_of_direct_childs[collection_node_idx] - 1])) {
+				// Collection has child collections, and this is not the first child
+
+				int nextChild = 1;
+				while (coll_last_written_child[collection_node_idx] != coll_child_order[collection_node_idx][nextChild - 1]) {
+					nextChild++;
+				}
+				coll_last_written_child[collection_node_idx] = coll_child_order[collection_node_idx][nextChild];
+				collection_node_idx = coll_child_order[collection_node_idx][nextChild];
+												
+				if (link_collection_nodes[collection_node_idx].IsAlias && (firstDelimiterNode == NULL)) {
+					// Alliased Collection (First node in link_collection_nodes -> Last entry in report descriptor output)
+					firstDelimiterNode = main_item_list;
+					coll_begin_lookup[collection_node_idx] = rd_append_main_item_node(0, 0, rd_item_node_collection, 0, collection_node_idx, rd_delimiter_usage, 0, &main_item_list);
+					coll_begin_lookup[collection_node_idx] = rd_append_main_item_node(0, 0, rd_item_node_collection, 0, collection_node_idx, rd_delimiter_close, 0, &main_item_list);
+					delimiterCloseNode = main_item_list;
+				}
+				else if (link_collection_nodes[collection_node_idx].IsAlias && (firstDelimiterNode != NULL)) {
+					coll_begin_lookup[collection_node_idx] = rd_insert_main_item_node(0, 0, rd_item_node_collection, 0, collection_node_idx, rd_delimiter_usage, 0, &firstDelimiterNode);
+				}
+				else if (!link_collection_nodes[collection_node_idx].IsAlias && (firstDelimiterNode != NULL)) {
+					coll_begin_lookup[collection_node_idx] = rd_insert_main_item_node(0, 0, rd_item_node_collection, 0, collection_node_idx, rd_delimiter_usage, 0, &firstDelimiterNode);
+					coll_begin_lookup[collection_node_idx] = rd_insert_main_item_node(0, 0, rd_item_node_collection, 0, collection_node_idx, rd_delimiter_open, 0, &firstDelimiterNode);
+					firstDelimiterNode = NULL;
+					main_item_list = delimiterCloseNode;
+					delimiterCloseNode = NULL; // Last entry of alias has .IsAlias == FALSE
+				}
+				if (!link_collection_nodes[collection_node_idx].IsAlias) {
+					coll_begin_lookup[collection_node_idx] = rd_append_main_item_node(0, 0, rd_item_node_collection, 0, collection_node_idx, rd_collection, 0, &main_item_list);
+					actual_coll_level++;
+				}
+			}
+			else {
+				actual_coll_level--;
+				coll_end_lookup[collection_node_idx] = rd_append_main_item_node(0, 0, rd_item_node_collection, 0, collection_node_idx, rd_collection_end, 0, &main_item_list);
+				collection_node_idx = link_collection_nodes[collection_node_idx].Parent;
+			}
+		}
+		free(coll_last_written_child);
+	}
+
+
+	// ****************************************************************
+	// Inserted Input/Output/Feature main items into the main_item_list
+	// in order of reconstructed bit positions
+	// ****************************************************************
+	for (HIDP_REPORT_TYPE rt_idx = 0; rt_idx < NUM_OF_HIDP_REPORT_TYPES; rt_idx++) {
+		// Add all value caps to node list
+		struct rd_main_item_node *firstDelimiterNode = NULL;
+		struct rd_main_item_node *delimiterCloseNode = NULL;
+		for (USHORT caps_idx = pp_data->caps_info[rt_idx].FirstCap; caps_idx < pp_data->caps_info[rt_idx].LastCap; caps_idx++) {
+			struct rd_main_item_node *coll_begin = coll_begin_lookup[pp_data->caps[caps_idx].LinkCollection];
+			int first_bit, last_bit;
+			first_bit = (pp_data->caps[caps_idx].BytePosition - 1) * 8 +
+				pp_data->caps[caps_idx].BitPosition;
+			last_bit = first_bit + pp_data->caps[caps_idx].ReportSize *
+				pp_data->caps[caps_idx].ReportCount - 1;
+
+			for (int child_idx = 0; child_idx < coll_number_of_direct_childs[pp_data->caps[caps_idx].LinkCollection]; child_idx++) {
+				// Determine in which section before/between/after child collection the item should be inserted
+				if (first_bit < coll_bit_range[coll_child_order[pp_data->caps[caps_idx].LinkCollection][child_idx]][pp_data->caps[caps_idx].ReportID][rt_idx]->FirstBit)
+				{
+					// Note, that the default value for undefined coll_bit_range is -1, which can't be greater than the bit position
+					break;
+				}
+				coll_begin = coll_end_lookup[coll_child_order[pp_data->caps[caps_idx].LinkCollection][child_idx]];
+			}
+			struct rd_main_item_node *list_node;
+			list_node = rd_search_main_item_list_for_bit_position(first_bit, (rd_main_items) rt_idx, pp_data->caps[caps_idx].ReportID, &coll_begin);
+
+			// In a HID Report Descriptor, the first usage declared is the most preferred usage for the control.
+			// While the order in the WIN32 capabiliy strutures is the opposite:
+			// Here the preferred usage is the last aliased usage in the sequence.
+
+			if (pp_data->caps[caps_idx].IsAlias && (firstDelimiterNode == NULL)) {
+				// Alliased Usage (First node in pp_data->caps -> Last entry in report descriptor output)
+				firstDelimiterNode = list_node;
+				rd_insert_main_item_node(first_bit, last_bit, rd_item_node_cap, caps_idx, pp_data->caps[caps_idx].LinkCollection, rd_delimiter_usage, pp_data->caps[caps_idx].ReportID, &list_node);
+				rd_insert_main_item_node(first_bit, last_bit, rd_item_node_cap, caps_idx, pp_data->caps[caps_idx].LinkCollection, rd_delimiter_close, pp_data->caps[caps_idx].ReportID, &list_node);
+				delimiterCloseNode = list_node;
+			} else if (pp_data->caps[caps_idx].IsAlias && (firstDelimiterNode != NULL)) {
+				rd_insert_main_item_node(first_bit, last_bit, rd_item_node_cap, caps_idx, pp_data->caps[caps_idx].LinkCollection, rd_delimiter_usage, pp_data->caps[caps_idx].ReportID, &list_node);
+			}
+			else if (!pp_data->caps[caps_idx].IsAlias && (firstDelimiterNode != NULL)) {
+				// Alliased Collection (Last node in pp_data->caps -> First entry in report descriptor output)
+				rd_insert_main_item_node(first_bit, last_bit, rd_item_node_cap, caps_idx, pp_data->caps[caps_idx].LinkCollection, rd_delimiter_usage, pp_data->caps[caps_idx].ReportID, &list_node);
+				rd_insert_main_item_node(first_bit, last_bit, rd_item_node_cap, caps_idx, pp_data->caps[caps_idx].LinkCollection, rd_delimiter_open, pp_data->caps[caps_idx].ReportID, &list_node);
+				firstDelimiterNode = NULL;
+				list_node = delimiterCloseNode;
+				delimiterCloseNode = NULL; // Last entry of alias has .IsAlias == FALSE
+			}
+			if (!pp_data->caps[caps_idx].IsAlias) {
+				rd_insert_main_item_node(first_bit, last_bit, rd_item_node_cap, caps_idx, pp_data->caps[caps_idx].LinkCollection, (rd_main_items) rt_idx, pp_data->caps[caps_idx].ReportID, &list_node);
+			}
+		}
+	}
+
+
+	// ***********************************************************
+	// Add const main items for padding to main_item_list
+	// -To fill all bit gaps
+	// -At each report end for 8bit padding
+	//  Note that information about the padding at the report end,
+	//  is not stored in the preparsed data, but in practice all
+	//  report descriptors seem to have it, as assumed here.
+	// ***********************************************************
+	{
+		int last_bit_position[NUM_OF_HIDP_REPORT_TYPES][256];
+		struct rd_main_item_node *last_report_item_lookup[NUM_OF_HIDP_REPORT_TYPES][256];
+		for (HIDP_REPORT_TYPE rt_idx = 0; rt_idx < NUM_OF_HIDP_REPORT_TYPES; rt_idx++) {
+			for (int reportid_idx = 0; reportid_idx < 256; reportid_idx++) {
+				last_bit_position[rt_idx][reportid_idx] = -1;
+				last_report_item_lookup[rt_idx][reportid_idx] = NULL;
+			}
+		}
+
+		struct rd_main_item_node *list = main_item_list; // List root;
+
+		while (list->next != NULL)
+		{
+			if ((list->MainItemType >= rd_input) &&
+				(list->MainItemType <= rd_feature)) {
+				// INPUT, OUTPUT or FEATURE
+				if (list->FirstBit != -1) {
+					if ((last_bit_position[list->MainItemType][list->ReportID] + 1 != list->FirstBit) &&
+						(last_report_item_lookup[list->MainItemType][list->ReportID] != NULL) &&
+						(last_report_item_lookup[list->MainItemType][list->ReportID]->FirstBit != list->FirstBit) // Happens in case of IsMultipleItemsForArray for multiple dedicated usages for a multi-button array
+						) {
+						struct rd_main_item_node *list_node = rd_search_main_item_list_for_bit_position(last_bit_position[list->MainItemType][list->ReportID], list->MainItemType, list->ReportID, &last_report_item_lookup[list->MainItemType][list->ReportID]);
+						rd_insert_main_item_node(last_bit_position[list->MainItemType][list->ReportID] + 1, list->FirstBit - 1, rd_item_node_padding, -1, 0, list->MainItemType, list->ReportID, &list_node);
+					}
+					last_bit_position[list->MainItemType][list->ReportID] = list->LastBit;
+					last_report_item_lookup[list->MainItemType][list->ReportID] = list;
+				}
+			}
+			list = list->next;
+		}
+		// Add 8 bit padding at each report end
+		for (HIDP_REPORT_TYPE rt_idx = 0; rt_idx < NUM_OF_HIDP_REPORT_TYPES; rt_idx++) {
+			for (int reportid_idx = 0; reportid_idx < 256; reportid_idx++) {
+				if (last_bit_position[rt_idx][reportid_idx] != -1) {
+					int padding = 8 - ((last_bit_position[rt_idx][reportid_idx] + 1) % 8);
+					if (padding < 8) {
+						// Insert padding item after item referenced in last_report_item_lookup
+						rd_insert_main_item_node(last_bit_position[rt_idx][reportid_idx] + 1, last_bit_position[rt_idx][reportid_idx] + padding, rd_item_node_padding, -1, 0, (rd_main_items) rt_idx, (unsigned char) reportid_idx, &last_report_item_lookup[rt_idx][reportid_idx]);
+					}
+				}
+			}
+		}
+	}
+
+
+	// ***********************************
+	// Encode the report descriptor output
+	// ***********************************
+	UCHAR last_report_id = 0;
+	USAGE last_usage_page = 0;
+	LONG last_physical_min = 0;// If both, Physical Minimum and Physical Maximum are 0, the logical limits should be taken as physical limits according USB HID spec 1.11 chapter 6.2.2.7
+	LONG last_physical_max = 0;
+	ULONG last_unit_exponent = 0; // If Unit Exponent is Undefined it should be considered as 0 according USB HID spec 1.11 chapter 6.2.2.7
+	ULONG last_unit = 0; // If the first nibble is 7, or second nibble of Unit is 0, the unit is None according USB HID spec 1.11 chapter 6.2.2.7
+	BOOLEAN inhibit_write_of_usage = FALSE; // Needed in case of delimited usage print, before the normal collection or cap
+	int report_count = 0;
+	while (main_item_list != NULL)
+	{
+		int rt_idx = main_item_list->MainItemType;
+		int	caps_idx = main_item_list->CapsIndex;
+		if (main_item_list->MainItemType == rd_collection) {
+			if (last_usage_page != link_collection_nodes[main_item_list->CollectionIndex].LinkUsagePage) {
+				// Write "Usage Page" at the begin of a collection - except it refers the same table as wrote last 
+				rd_write_short_item(rd_global_usage_page, link_collection_nodes[main_item_list->CollectionIndex].LinkUsagePage, &rpt_desc);
+				last_usage_page = link_collection_nodes[main_item_list->CollectionIndex].LinkUsagePage;
+			}
+			if (inhibit_write_of_usage) {
+				// Inhibit only once after DELIMITER statement
+				inhibit_write_of_usage = FALSE;
+			}
+			else {
+				// Write "Usage" of collection
+				rd_write_short_item(rd_local_usage, link_collection_nodes[main_item_list->CollectionIndex].LinkUsage, &rpt_desc);
+			}
+			// Write begin of "Collection" 
+			rd_write_short_item(rd_main_collection, link_collection_nodes[main_item_list->CollectionIndex].CollectionType, &rpt_desc);
+		}
+		else if (main_item_list->MainItemType == rd_collection_end) {
+			// Write "End Collection"
+			rd_write_short_item(rd_main_collection_end, 0, &rpt_desc);
+		}
+		else if (main_item_list->MainItemType == rd_delimiter_open) {
+			if (main_item_list->CollectionIndex != -1) {
+				// Write "Usage Page" inside of a collection delmiter section
+				if (last_usage_page != link_collection_nodes[main_item_list->CollectionIndex].LinkUsagePage) {
+					rd_write_short_item(rd_global_usage_page, link_collection_nodes[main_item_list->CollectionIndex].LinkUsagePage, &rpt_desc);
+					last_usage_page = link_collection_nodes[main_item_list->CollectionIndex].LinkUsagePage;
+				}
+			}
+			else if (main_item_list->CapsIndex != 0) {
+				// Write "Usage Page" inside of a main item delmiter section
+				if (pp_data->caps[caps_idx].UsagePage != last_usage_page) {
+					rd_write_short_item(rd_global_usage_page, pp_data->caps[caps_idx].UsagePage, &rpt_desc);
+					last_usage_page = pp_data->caps[caps_idx].UsagePage;
+				}
+			}
+			// Write "Delimiter Open"
+			rd_write_short_item(rd_local_delimiter, 1, &rpt_desc); // 1 = open set of aliased usages
+		}
+		else if (main_item_list->MainItemType == rd_delimiter_usage) {
+			if (main_item_list->CollectionIndex != -1) {
+				// Write aliased collection "Usage"
+				rd_write_short_item(rd_local_usage, link_collection_nodes[main_item_list->CollectionIndex].LinkUsage, &rpt_desc);
+			}  if (main_item_list->CapsIndex != 0) {
+				// Write aliased main item range from "Usage Minimum" to "Usage Maximum"
+				if (pp_data->caps[caps_idx].IsRange) {
+					rd_write_short_item(rd_local_usage_minimum, pp_data->caps[caps_idx].Range.UsageMin, &rpt_desc);
+					rd_write_short_item(rd_local_usage_maximum, pp_data->caps[caps_idx].Range.UsageMax, &rpt_desc);
+				}
+				else {
+					// Write single aliased main item "Usage"
+					rd_write_short_item(rd_local_usage, pp_data->caps[caps_idx].NotRange.Usage, &rpt_desc);
+				}
+			}
+		}
+		else if (main_item_list->MainItemType == rd_delimiter_close) {
+			// Write "Delimiter Close"
+			rd_write_short_item(rd_local_delimiter, 0, &rpt_desc); // 0 = close set of aliased usages
+			// Inhibit next usage write
+			inhibit_write_of_usage = TRUE;
+		}
+		else if (main_item_list->TypeOfNode == rd_item_node_padding) {
+			// Padding
+			// The preparsed data doesn't contain any information about padding. Therefore all undefined gaps
+			// in the reports are filled with the same style of constant padding. 
+
+			// Write "Report Size" with number of padding bits
+			rd_write_short_item(rd_global_report_size, (main_item_list->LastBit - main_item_list->FirstBit + 1), &rpt_desc);
+
+			// Write "Report Count" for padding always as 1
+			rd_write_short_item(rd_global_report_count, 1, &rpt_desc);
+
+			if (rt_idx == HidP_Input) {
+				// Write "Input" main item - We know it's Constant - We can only guess the other bits, but they don't matter in case of const
+				rd_write_short_item(rd_main_input, 0x03, &rpt_desc); // Const / Abs
+			}
+			else if (rt_idx == HidP_Output) {
+				// Write "Output" main item - We know it's Constant - We can only guess the other bits, but they don't matter in case of const
+				rd_write_short_item(rd_main_output, 0x03, &rpt_desc); // Const / Abs
+			}
+			else if (rt_idx == HidP_Feature) {
+				// Write "Feature" main item - We know it's Constant - We can only guess the other bits, but they don't matter in case of const
+				rd_write_short_item(rd_main_feature, 0x03, &rpt_desc); // Const / Abs
+			}
+			report_count = 0;
+		}
+		else if (pp_data->caps[caps_idx].IsButtonCap) {
+			// Button
+			// (The preparsed data contain different data for 1 bit Button caps, than for parametric Value caps)
+
+			if (last_report_id != pp_data->caps[caps_idx].ReportID) {
+				// Write "Report ID" if changed
+				rd_write_short_item(rd_global_report_id, pp_data->caps[caps_idx].ReportID, &rpt_desc);
+				last_report_id = pp_data->caps[caps_idx].ReportID;
+			}
+
+			// Write "Usage Page" when changed
+			if (pp_data->caps[caps_idx].UsagePage != last_usage_page) {
+				rd_write_short_item(rd_global_usage_page, pp_data->caps[caps_idx].UsagePage, &rpt_desc);
+				last_usage_page = pp_data->caps[caps_idx].UsagePage;
+			}
+
+			// Write only local report items for each cap, if ReportCount > 1
+			if (pp_data->caps[caps_idx].IsRange) {
+				report_count += (pp_data->caps[caps_idx].Range.DataIndexMax - pp_data->caps[caps_idx].Range.DataIndexMin);
+			}
+
+			if (inhibit_write_of_usage) {
+				// Inhibit only once after Delimiter - Reset flag
+				inhibit_write_of_usage = FALSE;
+			}
+			else {
+				if (pp_data->caps[caps_idx].IsRange) {
+					// Write range from "Usage Minimum" to "Usage Maximum"
+					rd_write_short_item(rd_local_usage_minimum, pp_data->caps[caps_idx].Range.UsageMin, &rpt_desc);
+					rd_write_short_item(rd_local_usage_maximum, pp_data->caps[caps_idx].Range.UsageMax, &rpt_desc);
+				}
+				else {
+					// Write single "Usage"
+					rd_write_short_item(rd_local_usage, pp_data->caps[caps_idx].NotRange.Usage, &rpt_desc);
+				}
+			}
+
+			if (pp_data->caps[caps_idx].IsDesignatorRange) {
+				// Write physical descriptor indices range from "Designator Minimum" to "Designator Maximum"
+				rd_write_short_item(rd_local_designator_minimum, pp_data->caps[caps_idx].Range.DesignatorMin, &rpt_desc);
+				rd_write_short_item(rd_local_designator_maximum, pp_data->caps[caps_idx].Range.DesignatorMax, &rpt_desc);
+			}
+			else if (pp_data->caps[caps_idx].NotRange.DesignatorIndex != 0) {
+				// Designator set 0 is a special descriptor set (of the HID Physical Descriptor),
+				// that specifies the number of additional descriptor sets.
+				// Therefore Designator Index 0 can never be a useful reference for a control and we can inhibit it.
+				// Write single "Designator Index"
+				rd_write_short_item(rd_local_designator_index, pp_data->caps[caps_idx].NotRange.DesignatorIndex, &rpt_desc);
+			}
+
+			if (pp_data->caps[caps_idx].IsStringRange) {
+				// Write range of indices of the USB string descriptor, from "String Minimum" to "String Maximum"
+				rd_write_short_item(rd_local_string_minimum, pp_data->caps[caps_idx].Range.StringMin, &rpt_desc);
+				rd_write_short_item(rd_local_string_maximum, pp_data->caps[caps_idx].Range.StringMax, &rpt_desc);
+			}
+			else if (pp_data->caps[caps_idx].NotRange.StringIndex != 0) {
+				// String Index 0 is a special entry of the USB string descriptor, that contains a list of supported languages,
+				// therefore Designator Index 0 can never be a useful reference for a control and we can inhibit it.
+				// Write single "String Index"
+				rd_write_short_item(rd_local_string, pp_data->caps[caps_idx].NotRange.StringIndex, &rpt_desc);
+			}
+
+			if ((main_item_list->next != NULL) &&
+				((int)main_item_list->next->MainItemType == rt_idx) &&
+				(main_item_list->next->TypeOfNode == rd_item_node_cap) &&
+				(pp_data->caps[main_item_list->next->CapsIndex].IsButtonCap) &&
+				(!pp_data->caps[caps_idx].IsRange) && // This node in list is no array
+				(!pp_data->caps[main_item_list->next->CapsIndex].IsRange) && // Next node in list is no array
+				(pp_data->caps[main_item_list->next->CapsIndex].UsagePage == pp_data->caps[caps_idx].UsagePage) &&
+				(pp_data->caps[main_item_list->next->CapsIndex].ReportID == pp_data->caps[caps_idx].ReportID) &&
+				(pp_data->caps[main_item_list->next->CapsIndex].BitField == pp_data->caps[caps_idx].BitField)
+				) {
+				if (main_item_list->next->FirstBit != main_item_list->FirstBit) {
+					// In case of IsMultipleItemsForArray for multiple dedicated usages for a multi-button array, the report count should be incremented 
+							
+					// Skip global items until any of them changes, than use ReportCount item to write the count of identical report fields
+					report_count++;
+				}
+			}
+			else {
+
+				if ((pp_data->caps[caps_idx].Button.LogicalMin == 0) &&
+					(pp_data->caps[caps_idx].Button.LogicalMax == 0)) {
+					// While a HID report descriptor must always contain LogicalMinimum and LogicalMaximum,
+					// the preparsed data contain both fields set to zero, for the case of simple buttons
+					// Write "Logical Minimum" set to 0 and "Logical Maximum" set to 1
+					rd_write_short_item(rd_global_logical_minimum, 0, &rpt_desc);
+					rd_write_short_item(rd_global_logical_maximum, 1, &rpt_desc);
+				}
+				else {
+					// Write logical range from "Logical Minimum" to "Logical Maximum"
+					rd_write_short_item(rd_global_logical_minimum, pp_data->caps[caps_idx].Button.LogicalMin, &rpt_desc);
+					rd_write_short_item(rd_global_logical_maximum, pp_data->caps[caps_idx].Button.LogicalMax, &rpt_desc);
+				}
+
+				// Write "Report Size"
+				rd_write_short_item(rd_global_report_size, pp_data->caps[caps_idx].ReportSize, &rpt_desc);
+
+				// Write "Report Count"
+				if (!pp_data->caps[caps_idx].IsRange) {
+					// Variable bit field with one bit per button
+					// In case of multiple usages with the same items, only "Usage" is written per cap, and "Report Count" is incremented
+					rd_write_short_item(rd_global_report_count, pp_data->caps[caps_idx].ReportCount + report_count, &rpt_desc);
+				}
+				else {
+					// Button array of "Report Size" x "Report Count
+					rd_write_short_item(rd_global_report_count, pp_data->caps[caps_idx].ReportCount, &rpt_desc);
+				}
+
+
+				// Buttons have only 1 bit and therefore no physical limits/units -> Set to undefined state
+				if (last_physical_min != 0) {
+					// Write "Physical Minimum", but only if changed
+					last_physical_min = 0;
+					rd_write_short_item(rd_global_physical_minimum, last_physical_min, &rpt_desc);
+				}
+				if (last_physical_max != 0) {
+					// Write "Physical Maximum", but only if changed
+					last_physical_max = 0;
+					rd_write_short_item(rd_global_physical_maximum, last_physical_max, &rpt_desc);
+				}
+				if (last_unit_exponent != 0) {
+					// Write "Unit Exponent", but only if changed
+					last_unit_exponent = 0;
+					rd_write_short_item(rd_global_unit_exponent, last_unit_exponent, &rpt_desc);
+				}
+				if (last_unit != 0) {
+					// Write "Unit",but only if changed
+					last_unit = 0;
+					rd_write_short_item(rd_global_unit, last_unit, &rpt_desc);
+				}
+
+				// Write "Input" main item
+				if (rt_idx == HidP_Input) {
+					rd_write_short_item(rd_main_input, pp_data->caps[caps_idx].BitField, &rpt_desc);
+				}
+				// Write "Output" main item
+				else if (rt_idx == HidP_Output) {
+					rd_write_short_item(rd_main_output, pp_data->caps[caps_idx].BitField, &rpt_desc);
+				}
+				// Write "Feature" main item
+				else if (rt_idx == HidP_Feature) {
+					rd_write_short_item(rd_main_feature, pp_data->caps[caps_idx].BitField, &rpt_desc);
+				}
+				report_count = 0;
+			}
+		}
+		else {
+
+			if (last_report_id != pp_data->caps[caps_idx].ReportID) {
+				// Write "Report ID" if changed
+				rd_write_short_item(rd_global_report_id, pp_data->caps[caps_idx].ReportID, &rpt_desc);
+				last_report_id = pp_data->caps[caps_idx].ReportID;
+			}
+
+			// Write "Usage Page" if changed
+			if (pp_data->caps[caps_idx].UsagePage != last_usage_page) {
+				rd_write_short_item(rd_global_usage_page, pp_data->caps[caps_idx].UsagePage, &rpt_desc);
+				last_usage_page = pp_data->caps[caps_idx].UsagePage;
+			}
+
+			if (inhibit_write_of_usage) {
+				// Inhibit only once after Delimiter - Reset flag
+				inhibit_write_of_usage = FALSE;
+			}
+			else {
+				if (pp_data->caps[caps_idx].IsRange) {
+					// Write usage range from "Usage Minimum" to "Usage Maximum"
+					rd_write_short_item(rd_local_usage_minimum, pp_data->caps[caps_idx].Range.UsageMin, &rpt_desc);
+					rd_write_short_item(rd_local_usage_maximum, pp_data->caps[caps_idx].Range.UsageMax, &rpt_desc);
+				}
+				else {
+					// Write single "Usage"
+					rd_write_short_item(rd_local_usage, pp_data->caps[caps_idx].NotRange.Usage, &rpt_desc);
+				}
+			}
+
+			if (pp_data->caps[caps_idx].IsDesignatorRange) {
+				// Write physical descriptor indices range from "Designator Minimum" to "Designator Maximum"
+				rd_write_short_item(rd_local_designator_minimum, pp_data->caps[caps_idx].Range.DesignatorMin, &rpt_desc);
+				rd_write_short_item(rd_local_designator_maximum, pp_data->caps[caps_idx].Range.DesignatorMax, &rpt_desc);
+			}
+			else if (pp_data->caps[caps_idx].NotRange.DesignatorIndex != 0) {
+				// Designator set 0 is a special descriptor set (of the HID Physical Descriptor),
+				// that specifies the number of additional descriptor sets.
+				// Therefore Designator Index 0 can never be a useful reference for a control and we can inhibit it.
+				// Write single "Designator Index"
+				rd_write_short_item(rd_local_designator_index, pp_data->caps[caps_idx].NotRange.DesignatorIndex, &rpt_desc);
+			}
+
+			if (pp_data->caps[caps_idx].IsStringRange) {
+				// Write range of indices of the USB string descriptor, from "String Minimum" to "String Maximum"
+				rd_write_short_item(rd_local_string_minimum, pp_data->caps[caps_idx].Range.StringMin, &rpt_desc);
+				rd_write_short_item(rd_local_string_maximum, pp_data->caps[caps_idx].Range.StringMax, &rpt_desc);
+			}
+			else if (pp_data->caps[caps_idx].NotRange.StringIndex != 0) {
+				// String Index 0 is a special entry of the USB string descriptor, that contains a list of supported languages,
+				// therefore Designator Index 0 can never be a useful reference for a control and we can inhibit it.
+				// Write single "String Index"
+				rd_write_short_item(rd_local_string, pp_data->caps[caps_idx].NotRange.StringIndex, &rpt_desc);
+			}
+
+			if ((pp_data->caps[caps_idx].BitField & 0x02) != 0x02) {
+				// In case of an value array overwrite "Report Count"
+				pp_data->caps[caps_idx].ReportCount = pp_data->caps[caps_idx].Range.DataIndexMax - pp_data->caps[caps_idx].Range.DataIndexMin + 1;
+			}
+
+
+			// Print only local report items for each cap, if ReportCount > 1
+			if ((main_item_list->next != NULL) &&
+				((int) main_item_list->next->MainItemType == rt_idx) &&
+				(main_item_list->next->TypeOfNode == rd_item_node_cap) &&
+				(!pp_data->caps[main_item_list->next->CapsIndex].IsButtonCap) &&
+				(!pp_data->caps[caps_idx].IsRange) && // This node in list is no array
+				(!pp_data->caps[main_item_list->next->CapsIndex].IsRange) && // Next node in list is no array
+				(pp_data->caps[main_item_list->next->CapsIndex].UsagePage == pp_data->caps[caps_idx].UsagePage) &&
+				(pp_data->caps[main_item_list->next->CapsIndex].NotButton.LogicalMin == pp_data->caps[caps_idx].NotButton.LogicalMin) &&
+				(pp_data->caps[main_item_list->next->CapsIndex].NotButton.LogicalMax == pp_data->caps[caps_idx].NotButton.LogicalMax) &&
+				(pp_data->caps[main_item_list->next->CapsIndex].NotButton.PhysicalMin == pp_data->caps[caps_idx].NotButton.PhysicalMin) &&
+				(pp_data->caps[main_item_list->next->CapsIndex].NotButton.PhysicalMax == pp_data->caps[caps_idx].NotButton.PhysicalMax) &&
+				(pp_data->caps[main_item_list->next->CapsIndex].UnitsExp == pp_data->caps[caps_idx].UnitsExp) &&
+				(pp_data->caps[main_item_list->next->CapsIndex].Units == pp_data->caps[caps_idx].Units) &&
+				(pp_data->caps[main_item_list->next->CapsIndex].ReportSize == pp_data->caps[caps_idx].ReportSize) &&
+				(pp_data->caps[main_item_list->next->CapsIndex].ReportID == pp_data->caps[caps_idx].ReportID) &&
+				(pp_data->caps[main_item_list->next->CapsIndex].BitField == pp_data->caps[caps_idx].BitField) &&
+				(pp_data->caps[main_item_list->next->CapsIndex].ReportCount == 1) &&
+				(pp_data->caps[caps_idx].ReportCount == 1)
+				) {
+				// Skip global items until any of them changes, than use ReportCount item to write the count of identical report fields
+				report_count++;
+			}
+			else {
+				// Value
+
+				// Write logical range from "Logical Minimum" to "Logical Maximum"
+				rd_write_short_item(rd_global_logical_minimum, pp_data->caps[caps_idx].NotButton.LogicalMin, &rpt_desc);
+				rd_write_short_item(rd_global_logical_maximum, pp_data->caps[caps_idx].NotButton.LogicalMax, &rpt_desc);
+
+				if ((last_physical_min != pp_data->caps[caps_idx].NotButton.PhysicalMin) ||
+					(last_physical_max != pp_data->caps[caps_idx].NotButton.PhysicalMax)) {
+					// Write range from "Physical Minimum" to " Physical Maximum", but only if one of them changed
+					rd_write_short_item(rd_global_physical_minimum, pp_data->caps[caps_idx].NotButton.PhysicalMin, &rpt_desc);
+					last_physical_min = pp_data->caps[caps_idx].NotButton.PhysicalMin;
+					rd_write_short_item(rd_global_physical_maximum, pp_data->caps[caps_idx].NotButton.PhysicalMax, &rpt_desc);
+					last_physical_max = pp_data->caps[caps_idx].NotButton.PhysicalMax;
+				}
+
+
+				if (last_unit_exponent != pp_data->caps[caps_idx].UnitsExp) {
+					// Write "Unit Exponent", but only if changed
+					rd_write_short_item(rd_global_unit_exponent, pp_data->caps[caps_idx].UnitsExp, &rpt_desc);
+					last_unit_exponent = pp_data->caps[caps_idx].UnitsExp;
+				}
+
+				if (last_unit != pp_data->caps[caps_idx].Units) {
+					// Write physical "Unit", but only if changed
+					rd_write_short_item(rd_global_unit, pp_data->caps[caps_idx].Units, &rpt_desc);
+					last_unit = pp_data->caps[caps_idx].Units;
+				}
+
+				// Write "Report Size"
+				rd_write_short_item(rd_global_report_size, pp_data->caps[caps_idx].ReportSize, &rpt_desc);
+
+				// Write "Report Count"
+				rd_write_short_item(rd_global_report_count, pp_data->caps[caps_idx].ReportCount + report_count, &rpt_desc);
+
+				if (rt_idx == HidP_Input) {
+					// Write "Input" main item
+					rd_write_short_item(rd_main_input, pp_data->caps[caps_idx].BitField, &rpt_desc);
+				}
+				else if (rt_idx == HidP_Output) {
+					// Write "Output" main item
+					rd_write_short_item(rd_main_output, pp_data->caps[caps_idx].BitField, &rpt_desc);
+				}
+				else if (rt_idx == HidP_Feature) {
+					// Write "Feature" main item
+					rd_write_short_item(rd_main_feature, pp_data->caps[caps_idx].BitField, &rpt_desc);
+				}
+				report_count = 0;
+			}
+		}
+
+		// Go to next item in main_item_list and free the memory of the actual item
+		struct rd_main_item_node *main_item_list_prev = main_item_list;
+		main_item_list = main_item_list->next;
+		free(main_item_list_prev);
+	}
+
+	// Free multidimensionable array: coll_bit_range[COLLECTION_INDEX][REPORT_ID][INPUT/OUTPUT/FEATURE]
+	// Free multidimensionable array: coll_child_order[COLLECTION_INDEX][DIRECT_CHILD_INDEX]
+	for (USHORT collection_node_idx = 0; collection_node_idx < pp_data->NumberLinkCollectionNodes; collection_node_idx++) {
+		for (int reportid_idx = 0; reportid_idx < 256; reportid_idx++) {
+			for (HIDP_REPORT_TYPE rt_idx = 0; rt_idx < NUM_OF_HIDP_REPORT_TYPES; rt_idx++) {
+				free(coll_bit_range[collection_node_idx][reportid_idx][rt_idx]);
+			}
+			free(coll_bit_range[collection_node_idx][reportid_idx]);
+		}
+		free(coll_bit_range[collection_node_idx]);
+		if (coll_number_of_direct_childs[collection_node_idx] != 0) free(coll_child_order[collection_node_idx]);
+	}
+	free(coll_bit_range);
+	free(coll_child_order);
+
+	// Free one dimensional arrays
+	free(coll_begin_lookup);
+	free(coll_end_lookup);
+	free(coll_levels);
+	free(coll_number_of_direct_childs);
+
+	return (int) rpt_desc.byte_idx;
+}

+ 238 - 0
src/hidapi/windows/hidapi_descriptor_reconstruct.h

@@ -0,0 +1,238 @@
+/*******************************************************
+ HIDAPI - Multi-Platform library for
+ communication with HID devices.
+
+ libusb/hidapi Team
+
+ Copyright 2022, All Rights Reserved.
+
+ At the discretion of the user of this library,
+ this software may be licensed under the terms of the
+ GNU General Public License v3, a BSD-Style license, or the
+ original HIDAPI license as outlined in the LICENSE.txt,
+ LICENSE-gpl3.txt, LICENSE-bsd.txt, and LICENSE-orig.txt
+ files located at the root of the source distribution.
+ These files may also be found in the public source
+ code repository located at:
+        https://github.com/libusb/hidapi .
+********************************************************/
+#ifndef HIDAPI_DESCRIPTOR_RECONSTRUCT_H__
+#define HIDAPI_DESCRIPTOR_RECONSTRUCT_H__
+
+#if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS)
+/* Do not warn about wcsncpy usage.
+   https://docs.microsoft.com/cpp/c-runtime-library/security-features-in-the-crt */
+#define _CRT_SECURE_NO_WARNINGS
+#endif
+
+#include "hidapi_winapi.h"
+
+#if _MSC_VER
+#pragma warning(push)
+#pragma warning(disable: 4200)
+#pragma warning(disable: 4201)
+#endif
+
+#include <windows.h>
+
+#include "hidapi_hidsdi.h"
+
+#define NUM_OF_HIDP_REPORT_TYPES 3
+
+typedef enum rd_items_ {
+	rd_main_input               = 0x80, /* 1000 00 nn */
+	rd_main_output              = 0x90, /* 1001 00 nn */
+	rd_main_feature             = 0xB0, /* 1011 00 nn */
+	rd_main_collection          = 0xA0, /* 1010 00 nn */
+	rd_main_collection_end      = 0xC0, /* 1100 00 nn */
+	rd_global_usage_page        = 0x04, /* 0000 01 nn */
+	rd_global_logical_minimum   = 0x14, /* 0001 01 nn */
+	rd_global_logical_maximum   = 0x24, /* 0010 01 nn */
+	rd_global_physical_minimum  = 0x34, /* 0011 01 nn */
+	rd_global_physical_maximum  = 0x44, /* 0100 01 nn */
+	rd_global_unit_exponent     = 0x54, /* 0101 01 nn */
+	rd_global_unit              = 0x64, /* 0110 01 nn */
+	rd_global_report_size       = 0x74, /* 0111 01 nn */
+	rd_global_report_id         = 0x84, /* 1000 01 nn */
+	rd_global_report_count      = 0x94, /* 1001 01 nn */
+	rd_global_push              = 0xA4, /* 1010 01 nn */
+	rd_global_pop               = 0xB4, /* 1011 01 nn */
+	rd_local_usage              = 0x08, /* 0000 10 nn */
+	rd_local_usage_minimum      = 0x18, /* 0001 10 nn */
+	rd_local_usage_maximum      = 0x28, /* 0010 10 nn */
+	rd_local_designator_index   = 0x38, /* 0011 10 nn */
+	rd_local_designator_minimum = 0x48, /* 0100 10 nn */
+	rd_local_designator_maximum = 0x58, /* 0101 10 nn */
+	rd_local_string             = 0x78, /* 0111 10 nn */
+	rd_local_string_minimum     = 0x88, /* 1000 10 nn */
+	rd_local_string_maximum     = 0x98, /* 1001 10 nn */
+	rd_local_delimiter          = 0xA8  /* 1010 10 nn */
+} rd_items;
+
+typedef enum rd_main_items_ {
+	rd_input = HidP_Input,
+	rd_output = HidP_Output,
+	rd_feature = HidP_Feature,
+	rd_collection,
+	rd_collection_end,
+	rd_delimiter_open,
+	rd_delimiter_usage,
+	rd_delimiter_close,
+} rd_main_items;
+
+typedef struct rd_bit_range_ {
+	int FirstBit;
+	int LastBit;
+} rd_bit_range;
+
+typedef enum rd_item_node_type_ {
+	rd_item_node_cap,
+	rd_item_node_padding,
+	rd_item_node_collection,
+} rd_node_type;
+
+struct rd_main_item_node {
+	int FirstBit; /* Position of first bit in report (counting from 0) */
+	int LastBit; /* Position of last bit in report (counting from 0) */
+	rd_node_type TypeOfNode; /* Information if caps index refers to the array of button caps, value caps,
+	                            or if the node is just a padding element to fill unused bit positions.
+	                            The node can also be a collection node without any bits in the report. */
+	int CapsIndex; /* Index in the array of caps */
+	int CollectionIndex; /* Index in the array of link collections */
+	rd_main_items MainItemType; /* Input, Output, Feature, Collection or Collection End */
+	unsigned char ReportID;
+	struct rd_main_item_node* next;
+};
+
+typedef struct hid_pp_caps_info_ {
+	USHORT FirstCap;
+	USHORT NumberOfCaps; // Includes empty caps after LastCap 
+	USHORT LastCap;
+	USHORT ReportByteLength;
+} hid_pp_caps_info, *phid_pp_caps_info;
+
+typedef struct hid_pp_link_collection_node_ {
+	USAGE  LinkUsage;
+	USAGE  LinkUsagePage;
+	USHORT Parent;
+	USHORT NumberOfChildren;
+	USHORT NextSibling;
+	USHORT FirstChild;
+	ULONG  CollectionType : 8;
+	ULONG  IsAlias : 1;
+	ULONG  Reserved : 23;
+	// Same as the public API structure HIDP_LINK_COLLECTION_NODE, but without PVOID UserContext at the end
+} hid_pp_link_collection_node, *phid_pp_link_collection_node;
+
+typedef struct hidp_unknown_token_ {
+	UCHAR Token; /* Specifies the one-byte prefix of a global item. */
+	UCHAR Reserved[3];
+	ULONG BitField; /* Specifies the data part of the global item. */
+} hidp_unknown_token, * phidp_unknown_token;
+
+typedef struct hid_pp_cap_ {
+	USAGE   UsagePage;
+	UCHAR   ReportID;
+	UCHAR   BitPosition;
+	USHORT  ReportSize; // WIN32 term for this is BitSize
+	USHORT  ReportCount;
+	USHORT  BytePosition;
+	USHORT  BitCount;
+	ULONG   BitField;
+	USHORT  NextBytePosition;
+	USHORT  LinkCollection;
+	USAGE   LinkUsagePage;
+	USAGE   LinkUsage;
+
+	// Start of 8 Flags in one byte
+	BOOLEAN IsMultipleItemsForArray:1;
+
+	BOOLEAN IsPadding:1;
+	BOOLEAN IsButtonCap:1;
+	BOOLEAN IsAbsolute:1;
+	BOOLEAN IsRange:1;
+	BOOLEAN IsAlias:1; // IsAlias is set to TRUE in the first n-1 capability structures added to the capability array. IsAlias set to FALSE in the nth capability structure.
+	BOOLEAN IsStringRange:1;
+	BOOLEAN IsDesignatorRange:1;
+	// End of 8 Flags in one byte
+	BOOLEAN Reserved1[3];
+
+	hidp_unknown_token UnknownTokens[4]; // 4 x 8 Byte
+
+	union {
+		struct {
+			USAGE  UsageMin;
+			USAGE  UsageMax;
+			USHORT StringMin;
+			USHORT StringMax;
+			USHORT DesignatorMin;
+			USHORT DesignatorMax;
+			USHORT DataIndexMin;
+			USHORT DataIndexMax;
+		} Range;
+		struct {
+			USAGE  Usage;
+			USAGE  Reserved1;
+			USHORT StringIndex;
+			USHORT Reserved2;
+			USHORT DesignatorIndex;
+			USHORT Reserved3;
+			USHORT DataIndex;
+			USHORT Reserved4;
+		} NotRange;
+	};
+	union {
+		struct {
+			LONG    LogicalMin;
+			LONG    LogicalMax;
+		} Button;
+		struct {
+			BOOLEAN HasNull;
+			UCHAR   Reserved4[3];
+			LONG    LogicalMin;
+			LONG    LogicalMax;
+			LONG    PhysicalMin;
+			LONG    PhysicalMax;
+		} NotButton;
+	};
+	ULONG   Units;
+	ULONG   UnitsExp;
+
+} hid_pp_cap, *phid_pp_cap;
+
+typedef struct hidp_preparsed_data_ {
+	UCHAR MagicKey[8];
+	USAGE Usage;
+	USAGE UsagePage;
+	USHORT Reserved[2];
+
+	// CAPS structure for Input, Output and Feature
+	hid_pp_caps_info caps_info[3];
+
+	USHORT FirstByteOfLinkCollectionArray;
+	USHORT NumberLinkCollectionNodes;
+
+#if defined(__MINGW32__) || defined(__CYGWIN__)
+	// MINGW fails with: Flexible array member in union not supported
+	// Solution: https://gcc.gnu.org/onlinedocs/gcc/Zero-Length.html
+	union {
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wpedantic"
+		hid_pp_cap caps[0];
+		hid_pp_link_collection_node LinkCollectionArray[0];
+#pragma GCC diagnostic pop
+	};
+#else
+	union {
+		hid_pp_cap caps[];
+		hid_pp_link_collection_node LinkCollectionArray[];
+	};
+#endif
+
+} hidp_preparsed_data;
+
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
+
+#endif

+ 7 - 0
src/hidapi/windows/hidapi_hidpi.h

@@ -29,6 +29,13 @@
 /* This part of the header mimics hidpi.h,
     but only what is used by HIDAPI */
 
+typedef enum _HIDP_REPORT_TYPE
+{
+    HidP_Input,
+    HidP_Output,
+    HidP_Feature
+} HIDP_REPORT_TYPE;
+
 typedef struct _HIDP_PREPARSED_DATA * PHIDP_PREPARSED_DATA;
 
 typedef struct _HIDP_CAPS

+ 2 - 1
src/hidapi/windows/hidapi_hidsdi.h

@@ -40,6 +40,8 @@ typedef struct _HIDD_ATTRIBUTES{
 	USHORT VersionNumber;
 } HIDD_ATTRIBUTES, *PHIDD_ATTRIBUTES;
 
+typedef struct _HIDP_PREPARSED_DATA * PHIDP_PREPARSED_DATA;
+
 typedef void (__stdcall *HidD_GetHidGuid_)(LPGUID hid_guid);
 typedef BOOLEAN (__stdcall *HidD_GetAttributes_)(HANDLE device, PHIDD_ATTRIBUTES attrib);
 typedef BOOLEAN (__stdcall *HidD_GetSerialNumberString_)(HANDLE device, PVOID buffer, ULONG buffer_len);
@@ -52,7 +54,6 @@ typedef BOOLEAN (__stdcall *HidD_GetIndexedString_)(HANDLE handle, ULONG string_
 typedef BOOLEAN (__stdcall *HidD_GetPreparsedData_)(HANDLE handle, PHIDP_PREPARSED_DATA *preparsed_data);
 typedef BOOLEAN (__stdcall *HidD_FreePreparsedData_)(PHIDP_PREPARSED_DATA preparsed_data);
 typedef BOOLEAN (__stdcall *HidD_SetNumInputBuffers_)(HANDLE handle, ULONG number_buffers);
-typedef BOOLEAN (__stdcall *HidD_SetOutputReport_)(HANDLE handle, PVOID buffer, ULONG buffer_len);
 
 #endif
 

+ 74 - 0
src/hidapi/windows/hidapi_winapi.h

@@ -0,0 +1,74 @@
+/*******************************************************
+ HIDAPI - Multi-Platform library for
+ communication with HID devices.
+
+ libusb/hidapi Team
+
+ Copyright 2022, All Rights Reserved.
+
+ At the discretion of the user of this library,
+ this software may be licensed under the terms of the
+ GNU General Public License v3, a BSD-Style license, or the
+ original HIDAPI license as outlined in the LICENSE.txt,
+ LICENSE-gpl3.txt, LICENSE-bsd.txt, and LICENSE-orig.txt
+ files located at the root of the source distribution.
+ These files may also be found in the public source
+ code repository located at:
+        https://github.com/libusb/hidapi .
+********************************************************/
+
+/** @file
+ * @defgroup API hidapi API
+ *
+ * Since version 0.12.0, @ref HID_API_VERSION >= HID_API_MAKE_VERSION(0, 12, 0)
+ */
+
+#ifndef HIDAPI_WINAPI_H__
+#define HIDAPI_WINAPI_H__
+
+#include <stdint.h>
+
+#include <guiddef.h>
+
+#include "hidapi.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+		/** @brief Get the container ID for a HID device.
+
+			Since version 0.12.0, @ref HID_API_VERSION >= HID_API_MAKE_VERSION(0, 12, 0)
+
+			This function returns the `DEVPKEY_Device_ContainerId` property of
+			the given device. This can be used to correlate different
+			interfaces/ports on the same hardware device.
+
+			@ingroup API
+			@param dev A device handle returned from hid_open().
+			@param guid The device's container ID on return.
+
+			@returns
+				This function returns 0 on success and -1 on error.
+		*/
+		int HID_API_EXPORT_CALL hid_winapi_get_container_id(hid_device *dev, GUID *container_id);
+
+		/**
+		 * @brief Reconstructs a HID Report Descriptor from a Win32 HIDP_PREPARSED_DATA structure.
+		 *  This reconstructed report descriptor is logical identical to the real report descriptor,
+		 *  but not byte wise identical.
+		 *
+		 * @param[in]  hidp_preparsed_data Pointer to the HIDP_PREPARSED_DATA to read, i.e.: the value of PHIDP_PREPARSED_DATA,
+		 *   as returned by HidD_GetPreparsedData WinAPI function.
+		 * @param      buf       Pointer to the buffer where the report descriptor should be stored.
+		 * @param[in]  buf_size  Size of the buffer. The recommended size for the buffer is @ref HID_API_MAX_REPORT_DESCRIPTOR_SIZE bytes.
+		 *
+		 * @return Returns size of reconstructed report descriptor if successful, -1 for error.
+		 */
+		int HID_API_EXPORT_CALL hid_winapi_descriptor_reconstruct_pp_data(void *hidp_preparsed_data, unsigned char *buf, size_t buf_size);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif

+ 196 - 196
src/hidapi/windows/hidtest.vcproj

@@ -1,196 +1,196 @@
-<?xml version="1.0" encoding="Windows-1252"?>
-<VisualStudioProject
-	ProjectType="Visual C++"
-	Version="9.00"
-	Name="hidtest"
-	ProjectGUID="{23E9FF6A-49D1-4993-B2B5-BBB992C6C712}"
-	RootNamespace="hidtest"
-	TargetFrameworkVersion="196613"
-	>
-	<Platforms>
-		<Platform
-			Name="Win32"
-		/>
-	</Platforms>
-	<ToolFiles>
-	</ToolFiles>
-	<Configurations>
-		<Configuration
-			Name="Debug|Win32"
-			OutputDirectory="$(SolutionDir)$(ConfigurationName)"
-			IntermediateDirectory="$(ConfigurationName)"
-			ConfigurationType="1"
-			CharacterSet="2"
-			>
-			<Tool
-				Name="VCPreBuildEventTool"
-			/>
-			<Tool
-				Name="VCCustomBuildTool"
-			/>
-			<Tool
-				Name="VCXMLDataGeneratorTool"
-			/>
-			<Tool
-				Name="VCWebServiceProxyGeneratorTool"
-			/>
-			<Tool
-				Name="VCMIDLTool"
-			/>
-			<Tool
-				Name="VCCLCompilerTool"
-				Optimization="0"
-				AdditionalIncludeDirectories="..\hidapi"
-				MinimalRebuild="true"
-				BasicRuntimeChecks="3"
-				RuntimeLibrary="3"
-				WarningLevel="3"
-				DebugInformationFormat="4"
-			/>
-			<Tool
-				Name="VCManagedResourceCompilerTool"
-			/>
-			<Tool
-				Name="VCResourceCompilerTool"
-			/>
-			<Tool
-				Name="VCPreLinkEventTool"
-			/>
-			<Tool
-				Name="VCLinkerTool"
-				AdditionalDependencies="hidapi.lib"
-				AdditionalLibraryDirectories="..\windows\Debug"
-				GenerateDebugInformation="true"
-				SubSystem="1"
-				TargetMachine="1"
-			/>
-			<Tool
-				Name="VCALinkTool"
-			/>
-			<Tool
-				Name="VCManifestTool"
-			/>
-			<Tool
-				Name="VCXDCMakeTool"
-			/>
-			<Tool
-				Name="VCBscMakeTool"
-			/>
-			<Tool
-				Name="VCFxCopTool"
-			/>
-			<Tool
-				Name="VCAppVerifierTool"
-			/>
-			<Tool
-				Name="VCPostBuildEventTool"
-				Description="Copying hidapi.dll to the local direcotry."
-				CommandLine=""
-			/>
-		</Configuration>
-		<Configuration
-			Name="Release|Win32"
-			OutputDirectory="$(SolutionDir)$(ConfigurationName)"
-			IntermediateDirectory="$(ConfigurationName)"
-			ConfigurationType="1"
-			CharacterSet="2"
-			WholeProgramOptimization="1"
-			>
-			<Tool
-				Name="VCPreBuildEventTool"
-			/>
-			<Tool
-				Name="VCCustomBuildTool"
-			/>
-			<Tool
-				Name="VCXMLDataGeneratorTool"
-			/>
-			<Tool
-				Name="VCWebServiceProxyGeneratorTool"
-			/>
-			<Tool
-				Name="VCMIDLTool"
-			/>
-			<Tool
-				Name="VCCLCompilerTool"
-				Optimization="2"
-				EnableIntrinsicFunctions="true"
-				AdditionalIncludeDirectories="..\hidapi"
-				RuntimeLibrary="2"
-				EnableFunctionLevelLinking="true"
-				WarningLevel="3"
-				DebugInformationFormat="3"
-			/>
-			<Tool
-				Name="VCManagedResourceCompilerTool"
-			/>
-			<Tool
-				Name="VCResourceCompilerTool"
-			/>
-			<Tool
-				Name="VCPreLinkEventTool"
-			/>
-			<Tool
-				Name="VCLinkerTool"
-				AdditionalDependencies="hidapi.lib"
-				AdditionalLibraryDirectories="..\windows\Release"
-				GenerateDebugInformation="true"
-				SubSystem="1"
-				OptimizeReferences="2"
-				EnableCOMDATFolding="2"
-				TargetMachine="1"
-			/>
-			<Tool
-				Name="VCALinkTool"
-			/>
-			<Tool
-				Name="VCManifestTool"
-			/>
-			<Tool
-				Name="VCXDCMakeTool"
-			/>
-			<Tool
-				Name="VCBscMakeTool"
-			/>
-			<Tool
-				Name="VCFxCopTool"
-			/>
-			<Tool
-				Name="VCAppVerifierTool"
-			/>
-			<Tool
-				Name="VCPostBuildEventTool"
-				Description="Copying hidapi.dll to the local direcotry."
-				CommandLine=""
-			/>
-		</Configuration>
-	</Configurations>
-	<References>
-	</References>
-	<Files>
-		<Filter
-			Name="Source Files"
-			Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx"
-			UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"
-			>
-			<File
-				RelativePath="..\hidtest\hidtest.cpp"
-				>
-			</File>
-		</Filter>
-		<Filter
-			Name="Header Files"
-			Filter="h;hpp;hxx;hm;inl;inc;xsd"
-			UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"
-			>
-		</Filter>
-		<Filter
-			Name="Resource Files"
-			Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav"
-			UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}"
-			>
-		</Filter>
-	</Files>
-	<Globals>
-	</Globals>
-</VisualStudioProject>
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+	ProjectType="Visual C++"
+	Version="9.00"
+	Name="hidtest"
+	ProjectGUID="{23E9FF6A-49D1-4993-B2B5-BBB992C6C712}"
+	RootNamespace="hidtest"
+	TargetFrameworkVersion="196613"
+	>
+	<Platforms>
+		<Platform
+			Name="Win32"
+		/>
+	</Platforms>
+	<ToolFiles>
+	</ToolFiles>
+	<Configurations>
+		<Configuration
+			Name="Debug|Win32"
+			OutputDirectory="$(SolutionDir)$(ConfigurationName)"
+			IntermediateDirectory="$(ConfigurationName)"
+			ConfigurationType="1"
+			CharacterSet="2"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				Optimization="0"
+				AdditionalIncludeDirectories="..\hidapi;."
+				MinimalRebuild="true"
+				BasicRuntimeChecks="3"
+				RuntimeLibrary="3"
+				WarningLevel="3"
+				DebugInformationFormat="4"
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLinkerTool"
+				AdditionalDependencies="hidapi.lib"
+				AdditionalLibraryDirectories="..\windows\Debug"
+				GenerateDebugInformation="true"
+				SubSystem="1"
+				TargetMachine="1"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCManifestTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCAppVerifierTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+				Description="Copying hidapi.dll to the local directory."
+				CommandLine=""
+			/>
+		</Configuration>
+		<Configuration
+			Name="Release|Win32"
+			OutputDirectory="$(SolutionDir)$(ConfigurationName)"
+			IntermediateDirectory="$(ConfigurationName)"
+			ConfigurationType="1"
+			CharacterSet="2"
+			WholeProgramOptimization="1"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				Optimization="2"
+				EnableIntrinsicFunctions="true"
+				AdditionalIncludeDirectories="..\hidapi;."
+				RuntimeLibrary="2"
+				EnableFunctionLevelLinking="true"
+				WarningLevel="3"
+				DebugInformationFormat="3"
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLinkerTool"
+				AdditionalDependencies="hidapi.lib"
+				AdditionalLibraryDirectories="..\windows\Release"
+				GenerateDebugInformation="true"
+				SubSystem="1"
+				OptimizeReferences="2"
+				EnableCOMDATFolding="2"
+				TargetMachine="1"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCManifestTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCAppVerifierTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+				Description="Copying hidapi.dll to the local directory."
+				CommandLine=""
+			/>
+		</Configuration>
+	</Configurations>
+	<References>
+	</References>
+	<Files>
+		<Filter
+			Name="Source Files"
+			Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx"
+			UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"
+			>
+			<File
+				RelativePath="..\hidtest\test.c"
+				>
+			</File>
+		</Filter>
+		<Filter
+			Name="Header Files"
+			Filter="h;hpp;hxx;hm;inl;inc;xsd"
+			UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"
+			>
+		</Filter>
+		<Filter
+			Name="Resource Files"
+			Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav"
+			UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}"
+			>
+		</Filter>
+	</Files>
+	<Globals>
+	</Globals>
+</VisualStudioProject>

Some files were not shown because too many files changed in this diff