From 3b7b8f3c09491f4076b38baf170ad9f3aa249bad Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Wed, 24 May 2023 07:28:55 -0700 Subject: [PATCH] Updated hidapi to 0.14.0 release Upstream: https://github.com/libusb/hidapi/releases/tag/hidapi-0.14.0 --- src/hidapi/.appveyor.yml | 31 + src/hidapi/.builds/freebsd.yml | 34 + src/hidapi/.builds/netbsd.yml | 18 + src/hidapi/.builds/openbsd.yml | 19 + src/hidapi/.cirrus.yml | 33 + src/hidapi/.gitattributes | 7 + src/hidapi/.github/workflows/builds.yml | 540 + src/hidapi/.github/workflows/checks.yml | 196 + src/hidapi/.github/workflows/docs.yaml | 58 + src/hidapi/.gitignore | 32 + src/hidapi/AUTHORS.txt | 4 +- src/hidapi/BUILD.autotools.md | 114 + src/hidapi/BUILD.cmake.md | 280 + src/hidapi/BUILD.md | 127 + src/hidapi/CMakeLists.txt | 105 + src/hidapi/HACKING.txt | 26 +- src/hidapi/Makefile.am | 10 +- src/hidapi/README.md | 196 + src/hidapi/README.txt | 339 - src/hidapi/VERSION | 1 + src/hidapi/android/hid.cpp | 1443 -- src/hidapi/android/hid.h | 39 - src/hidapi/android/jni/Android.mk | 16 - src/hidapi/android/jni/Application.mk | 2 - src/hidapi/android/project.properties | 14 - src/hidapi/configure.ac | 68 +- src/hidapi/dist/hidapi.podspec | 31 + .../documentation/cmake-gui-drop-down.png | Bin 0 -> 22316 bytes .../documentation/cmake-gui-highlights.png | Bin 0 -> 77327 bytes src/hidapi/doxygen/Doxyfile | 2832 ++-- src/hidapi/doxygen/main_page.md | 13 + src/hidapi/hidapi/hidapi.h | 319 +- src/hidapi/hidtest/.gitignore | 17 + src/hidapi/hidtest/CMakeLists.txt | 40 + src/hidapi/hidtest/Makefile.am | 14 +- src/hidapi/hidtest/hidtest.cpp | 194 - src/hidapi/hidtest/test.c | 316 + src/hidapi/ios/Makefile-manual | 32 - src/hidapi/ios/Makefile.am | 9 - src/hidapi/ios/hid.m | 996 -- src/hidapi/libusb/.gitignore | 8 + src/hidapi/libusb/CMakeLists.txt | 107 + src/hidapi/libusb/Makefile-manual | 4 + src/hidapi/libusb/Makefile.am | 9 +- src/hidapi/libusb/Makefile.freebsd | 15 +- src/hidapi/libusb/Makefile.haiku | 39 + src/hidapi/libusb/Makefile.linux | 17 +- src/hidapi/libusb/hid.c | 1325 +- src/hidapi/libusb/hidapi_libusb.h | 56 + src/hidapi/libusb/hidusb.cpp | 3 - src/hidapi/linux/.gitignore | 18 + src/hidapi/linux/CMakeLists.txt | 38 + src/hidapi/linux/Makefile-manual | 17 +- src/hidapi/linux/README.txt | 59 - src/hidapi/linux/hid.c | 1352 +- src/hidapi/linux/hidraw.cpp | 3 - src/hidapi/m4/.gitignore | 5 + src/hidapi/mac/.gitignore | 17 + src/hidapi/mac/CMakeLists.txt | 48 + src/hidapi/mac/Makefile-manual | 17 +- src/hidapi/mac/hid.c | 1655 ++- src/hidapi/mac/hidapi_darwin.h | 98 + src/hidapi/meson.build | 22 + src/hidapi/pc/.gitignore | 1 + src/hidapi/pc/hidapi-hidraw.pc.in | 1 + src/hidapi/pc/hidapi-libusb.pc.in | 1 + src/hidapi/pc/hidapi.pc.in | 1 + src/hidapi/src/CMakeLists.txt | 193 + src/hidapi/src/cmake/hidapi-config.cmake.in | 61 + src/hidapi/subprojects/README.md | 2 + .../hidapi_build_cmake/CMakeLists.txt | 10 + src/hidapi/testgui/.gitignore | 20 + src/hidapi/testgui/Makefile.mingw | 2 +- src/hidapi/testgui/copy_to_bundle.sh | 3 +- src/hidapi/testgui/mac_support.cpp | 134 - src/hidapi/testgui/mac_support_cocoa.m | 13 +- src/hidapi/testgui/start.sh | 2 - src/hidapi/testgui/testgui.sln | 40 +- src/hidapi/testgui/testgui.vcproj | 434 +- src/hidapi/udev/69-hid.rules | 36 + src/hidapi/udev/99-hid.rules | 33 - src/hidapi/windows/.gitignore | 17 + src/hidapi/windows/CMakeLists.txt | 63 + src/hidapi/windows/Makefile.am | 1 - src/hidapi/windows/Makefile.mingw | 17 +- src/hidapi/windows/ddk_build/hidapi.def | 17 - src/hidapi/windows/ddk_build/makefile | 49 - src/hidapi/windows/ddk_build/sources | 23 - src/hidapi/windows/hid.c | 1568 +-- src/hidapi/windows/hidapi.rc | 35 + src/hidapi/windows/hidapi.sln | 70 +- src/hidapi/windows/hidapi.vcproj | 401 +- src/hidapi/windows/hidapi.vcxproj | 200 + src/hidapi/windows/hidapi_cfgmgr32.h | 30 +- .../windows/hidapi_descriptor_reconstruct.c | 987 ++ .../windows/hidapi_descriptor_reconstruct.h | 238 + src/hidapi/windows/hidapi_hidpi.h | 7 + src/hidapi/windows/hidapi_hidsdi.h | 3 +- src/hidapi/windows/hidapi_winapi.h | 74 + src/hidapi/windows/hidtest.vcproj | 392 +- src/hidapi/windows/hidtest.vcxproj | 176 + .../windows/pp_data_dump/CMakeLists.txt | 15 + src/hidapi/windows/pp_data_dump/README.md | 122 + .../windows/pp_data_dump/pp_data_dump.c | 238 + src/hidapi/windows/test/CMakeLists.txt | 76 + .../test/data/045E_02FF_0005_0001.pp_data | 420 + .../045E_02FF_0005_0001_expected.rpt_desc | 12 + .../data/045E_02FF_0005_0001_real.rpt_desc | 64 + .../test/data/046A_0011_0006_0001.pp_data | 183 + .../046A_0011_0006_0001_expected.rpt_desc | 7 + .../data/046A_0011_0006_0001_real.rpt_desc | 7 + .../test/data/046D_0A37_0001_000C.pp_data | 532 + .../046D_0A37_0001_000C_expected.rpt_desc | 16 + .../data/046D_0A37_0001_000C_real.rpt_desc | 61 + .../test/data/046D_B010_0001_000C.pp_data | 97 + .../046D_B010_0001_000C_expected.rpt_desc | 3 + .../data/046D_B010_0001_000C_real.rpt_desc | 38 + .../test/data/046D_B010_0001_FF00.pp_data | 139 + .../046D_B010_0001_FF00_expected.rpt_desc | 4 + .../data/046D_B010_0001_FF00_real.rpt_desc | 39 + .../test/data/046D_B010_0002_0001.pp_data | 302 + .../046D_B010_0002_0001_expected.rpt_desc | 8 + .../data/046D_B010_0002_0001_real.rpt_desc | 61 + .../test/data/046D_B010_0002_FF00.pp_data | 139 + .../046D_B010_0002_FF00_expected.rpt_desc | 4 + .../data/046D_B010_0002_FF00_real.rpt_desc | 39 + .../test/data/046D_B010_0006_0001.pp_data | 185 + .../046D_B010_0006_0001_expected.rpt_desc | 7 + .../data/046D_B010_0006_0001_real.rpt_desc | 58 + .../test/data/046D_C077_0002_0001.pp_data | 252 + .../046D_C077_0002_0001_expected.rpt_desc | 5 + .../data/046D_C077_0002_0001_real.rpt_desc | 24 + .../test/data/046D_C283_0004_0001.pp_data | 520 + .../046D_C283_0004_0001_expected.rpt_desc | 18 + .../data/046D_C283_0004_0001_real.rpt_desc | 18 + .../test/data/046D_C52F_0001_000C.pp_data | 93 + .../046D_C52F_0001_000C_expected.rpt_desc | 3 + .../data/046D_C52F_0001_000C_real.rpt_desc | 12 + .../test/data/046D_C52F_0001_FF00.pp_data | 139 + .../046D_C52F_0001_FF00_expected.rpt_desc | 4 + .../data/046D_C52F_0001_FF00_real.rpt_desc | 13 + .../test/data/046D_C52F_0002_0001.pp_data | 302 + .../046D_C52F_0002_0001_expected.rpt_desc | 8 + .../data/046D_C52F_0002_0001_real.rpt_desc | 33 + .../test/data/046D_C52F_0002_FF00.pp_data | 139 + .../046D_C52F_0002_FF00_expected.rpt_desc | 4 + .../data/046D_C52F_0002_FF00_real.rpt_desc | 13 + .../test/data/046D_C534_0001_000C.pp_data | 93 + .../046D_C534_0001_000C_expected.rpt_desc | 3 + .../data/046D_C534_0001_000C_real.rpt_desc | 18 + .../test/data/046D_C534_0001_FF00.pp_data | 139 + .../046D_C534_0001_FF00_expected.rpt_desc | 4 + .../data/046D_C534_0001_FF00_real.rpt_desc | 20 + .../test/data/046D_C534_0002_0001.pp_data | 302 + .../046D_C534_0002_0001_expected.rpt_desc | 8 + .../data/046D_C534_0002_0001_real.rpt_desc | 44 + .../test/data/046D_C534_0002_FF00.pp_data | 139 + .../046D_C534_0002_FF00_expected.rpt_desc | 4 + .../data/046D_C534_0002_FF00_real.rpt_desc | 22 + .../test/data/046D_C534_0006_0001.pp_data | 185 + .../046D_C534_0006_0001_expected.rpt_desc | 7 + .../data/046D_C534_0006_0001_real.rpt_desc | 42 + .../test/data/046D_C534_0080_0001.pp_data | 185 + .../046D_C534_0080_0001_expected.rpt_desc | 4 + .../data/046D_C534_0080_0001_real.rpt_desc | 22 + .../test/data/047F_C056_0001_000C.pp_data | 385 + .../047F_C056_0001_000C_expected.rpt_desc | 10 + .../data/047F_C056_0001_000C_real.rpt_desc | 47 + .../test/data/047F_C056_0003_FFA0.pp_data | 1255 ++ .../047F_C056_0003_FFA0_expected.rpt_desc | 24 + .../data/047F_C056_0003_FFA0_real.rpt_desc | 113 + .../test/data/047F_C056_0005_000B.pp_data | 461 + .../047F_C056_0005_000B_expected.rpt_desc | 17 + .../data/047F_C056_0005_000B_real.rpt_desc | 68 + .../test/data/17CC_1130_0000_FF01.pp_data | 11508 ++++++++++++++++ .../17CC_1130_0000_FF01_expected.rpt_desc | 75 + .../data/17CC_1130_0000_FF01_real.rpt_desc | 381 + .../test/hid_report_reconstructor_test.c | 561 + 178 files changed, 31717 insertions(+), 7757 deletions(-) create mode 100644 src/hidapi/.appveyor.yml create mode 100644 src/hidapi/.builds/freebsd.yml create mode 100644 src/hidapi/.builds/netbsd.yml create mode 100644 src/hidapi/.builds/openbsd.yml create mode 100644 src/hidapi/.cirrus.yml create mode 100644 src/hidapi/.gitattributes create mode 100644 src/hidapi/.github/workflows/builds.yml create mode 100644 src/hidapi/.github/workflows/checks.yml create mode 100644 src/hidapi/.github/workflows/docs.yaml create mode 100644 src/hidapi/.gitignore create mode 100644 src/hidapi/BUILD.autotools.md create mode 100644 src/hidapi/BUILD.cmake.md create mode 100644 src/hidapi/BUILD.md create mode 100644 src/hidapi/CMakeLists.txt create mode 100644 src/hidapi/README.md delete mode 100644 src/hidapi/README.txt create mode 100644 src/hidapi/VERSION delete mode 100644 src/hidapi/android/hid.cpp delete mode 100644 src/hidapi/android/hid.h delete mode 100644 src/hidapi/android/jni/Android.mk delete mode 100644 src/hidapi/android/jni/Application.mk delete mode 100644 src/hidapi/android/project.properties create mode 100644 src/hidapi/dist/hidapi.podspec create mode 100644 src/hidapi/documentation/cmake-gui-drop-down.png create mode 100644 src/hidapi/documentation/cmake-gui-highlights.png create mode 100644 src/hidapi/doxygen/main_page.md create mode 100644 src/hidapi/hidtest/.gitignore create mode 100644 src/hidapi/hidtest/CMakeLists.txt delete mode 100644 src/hidapi/hidtest/hidtest.cpp create mode 100644 src/hidapi/hidtest/test.c delete mode 100644 src/hidapi/ios/Makefile-manual delete mode 100644 src/hidapi/ios/Makefile.am delete mode 100644 src/hidapi/ios/hid.m create mode 100644 src/hidapi/libusb/.gitignore create mode 100644 src/hidapi/libusb/CMakeLists.txt create mode 100644 src/hidapi/libusb/Makefile.haiku create mode 100644 src/hidapi/libusb/hidapi_libusb.h delete mode 100644 src/hidapi/libusb/hidusb.cpp create mode 100644 src/hidapi/linux/.gitignore create mode 100644 src/hidapi/linux/CMakeLists.txt delete mode 100644 src/hidapi/linux/README.txt delete mode 100644 src/hidapi/linux/hidraw.cpp create mode 100644 src/hidapi/m4/.gitignore create mode 100644 src/hidapi/mac/.gitignore create mode 100644 src/hidapi/mac/CMakeLists.txt create mode 100644 src/hidapi/mac/hidapi_darwin.h create mode 100644 src/hidapi/meson.build create mode 100644 src/hidapi/pc/.gitignore create mode 100644 src/hidapi/src/CMakeLists.txt create mode 100644 src/hidapi/src/cmake/hidapi-config.cmake.in create mode 100644 src/hidapi/subprojects/README.md create mode 100644 src/hidapi/subprojects/hidapi_build_cmake/CMakeLists.txt create mode 100644 src/hidapi/testgui/.gitignore delete mode 100644 src/hidapi/testgui/mac_support.cpp delete mode 100755 src/hidapi/testgui/start.sh create mode 100644 src/hidapi/udev/69-hid.rules delete mode 100644 src/hidapi/udev/99-hid.rules create mode 100644 src/hidapi/windows/.gitignore create mode 100644 src/hidapi/windows/CMakeLists.txt delete mode 100644 src/hidapi/windows/ddk_build/hidapi.def delete mode 100644 src/hidapi/windows/ddk_build/makefile delete mode 100644 src/hidapi/windows/ddk_build/sources create mode 100644 src/hidapi/windows/hidapi.rc create mode 100644 src/hidapi/windows/hidapi.vcxproj create mode 100644 src/hidapi/windows/hidapi_descriptor_reconstruct.c create mode 100644 src/hidapi/windows/hidapi_descriptor_reconstruct.h create mode 100644 src/hidapi/windows/hidapi_winapi.h create mode 100644 src/hidapi/windows/hidtest.vcxproj create mode 100644 src/hidapi/windows/pp_data_dump/CMakeLists.txt create mode 100644 src/hidapi/windows/pp_data_dump/README.md create mode 100644 src/hidapi/windows/pp_data_dump/pp_data_dump.c create mode 100644 src/hidapi/windows/test/CMakeLists.txt create mode 100644 src/hidapi/windows/test/data/045E_02FF_0005_0001.pp_data create mode 100644 src/hidapi/windows/test/data/045E_02FF_0005_0001_expected.rpt_desc create mode 100644 src/hidapi/windows/test/data/045E_02FF_0005_0001_real.rpt_desc create mode 100644 src/hidapi/windows/test/data/046A_0011_0006_0001.pp_data create mode 100644 src/hidapi/windows/test/data/046A_0011_0006_0001_expected.rpt_desc create mode 100644 src/hidapi/windows/test/data/046A_0011_0006_0001_real.rpt_desc create mode 100644 src/hidapi/windows/test/data/046D_0A37_0001_000C.pp_data create mode 100644 src/hidapi/windows/test/data/046D_0A37_0001_000C_expected.rpt_desc create mode 100644 src/hidapi/windows/test/data/046D_0A37_0001_000C_real.rpt_desc create mode 100644 src/hidapi/windows/test/data/046D_B010_0001_000C.pp_data create mode 100644 src/hidapi/windows/test/data/046D_B010_0001_000C_expected.rpt_desc create mode 100644 src/hidapi/windows/test/data/046D_B010_0001_000C_real.rpt_desc create mode 100644 src/hidapi/windows/test/data/046D_B010_0001_FF00.pp_data create mode 100644 src/hidapi/windows/test/data/046D_B010_0001_FF00_expected.rpt_desc create mode 100644 src/hidapi/windows/test/data/046D_B010_0001_FF00_real.rpt_desc create mode 100644 src/hidapi/windows/test/data/046D_B010_0002_0001.pp_data create mode 100644 src/hidapi/windows/test/data/046D_B010_0002_0001_expected.rpt_desc create mode 100644 src/hidapi/windows/test/data/046D_B010_0002_0001_real.rpt_desc create mode 100644 src/hidapi/windows/test/data/046D_B010_0002_FF00.pp_data create mode 100644 src/hidapi/windows/test/data/046D_B010_0002_FF00_expected.rpt_desc create mode 100644 src/hidapi/windows/test/data/046D_B010_0002_FF00_real.rpt_desc create mode 100644 src/hidapi/windows/test/data/046D_B010_0006_0001.pp_data create mode 100644 src/hidapi/windows/test/data/046D_B010_0006_0001_expected.rpt_desc create mode 100644 src/hidapi/windows/test/data/046D_B010_0006_0001_real.rpt_desc create mode 100644 src/hidapi/windows/test/data/046D_C077_0002_0001.pp_data create mode 100644 src/hidapi/windows/test/data/046D_C077_0002_0001_expected.rpt_desc create mode 100644 src/hidapi/windows/test/data/046D_C077_0002_0001_real.rpt_desc create mode 100644 src/hidapi/windows/test/data/046D_C283_0004_0001.pp_data create mode 100644 src/hidapi/windows/test/data/046D_C283_0004_0001_expected.rpt_desc create mode 100644 src/hidapi/windows/test/data/046D_C283_0004_0001_real.rpt_desc create mode 100644 src/hidapi/windows/test/data/046D_C52F_0001_000C.pp_data create mode 100644 src/hidapi/windows/test/data/046D_C52F_0001_000C_expected.rpt_desc create mode 100644 src/hidapi/windows/test/data/046D_C52F_0001_000C_real.rpt_desc create mode 100644 src/hidapi/windows/test/data/046D_C52F_0001_FF00.pp_data create mode 100644 src/hidapi/windows/test/data/046D_C52F_0001_FF00_expected.rpt_desc create mode 100644 src/hidapi/windows/test/data/046D_C52F_0001_FF00_real.rpt_desc create mode 100644 src/hidapi/windows/test/data/046D_C52F_0002_0001.pp_data create mode 100644 src/hidapi/windows/test/data/046D_C52F_0002_0001_expected.rpt_desc create mode 100644 src/hidapi/windows/test/data/046D_C52F_0002_0001_real.rpt_desc create mode 100644 src/hidapi/windows/test/data/046D_C52F_0002_FF00.pp_data create mode 100644 src/hidapi/windows/test/data/046D_C52F_0002_FF00_expected.rpt_desc create mode 100644 src/hidapi/windows/test/data/046D_C52F_0002_FF00_real.rpt_desc create mode 100644 src/hidapi/windows/test/data/046D_C534_0001_000C.pp_data create mode 100644 src/hidapi/windows/test/data/046D_C534_0001_000C_expected.rpt_desc create mode 100644 src/hidapi/windows/test/data/046D_C534_0001_000C_real.rpt_desc create mode 100644 src/hidapi/windows/test/data/046D_C534_0001_FF00.pp_data create mode 100644 src/hidapi/windows/test/data/046D_C534_0001_FF00_expected.rpt_desc create mode 100644 src/hidapi/windows/test/data/046D_C534_0001_FF00_real.rpt_desc create mode 100644 src/hidapi/windows/test/data/046D_C534_0002_0001.pp_data create mode 100644 src/hidapi/windows/test/data/046D_C534_0002_0001_expected.rpt_desc create mode 100644 src/hidapi/windows/test/data/046D_C534_0002_0001_real.rpt_desc create mode 100644 src/hidapi/windows/test/data/046D_C534_0002_FF00.pp_data create mode 100644 src/hidapi/windows/test/data/046D_C534_0002_FF00_expected.rpt_desc create mode 100644 src/hidapi/windows/test/data/046D_C534_0002_FF00_real.rpt_desc create mode 100644 src/hidapi/windows/test/data/046D_C534_0006_0001.pp_data create mode 100644 src/hidapi/windows/test/data/046D_C534_0006_0001_expected.rpt_desc create mode 100644 src/hidapi/windows/test/data/046D_C534_0006_0001_real.rpt_desc create mode 100644 src/hidapi/windows/test/data/046D_C534_0080_0001.pp_data create mode 100644 src/hidapi/windows/test/data/046D_C534_0080_0001_expected.rpt_desc create mode 100644 src/hidapi/windows/test/data/046D_C534_0080_0001_real.rpt_desc create mode 100644 src/hidapi/windows/test/data/047F_C056_0001_000C.pp_data create mode 100644 src/hidapi/windows/test/data/047F_C056_0001_000C_expected.rpt_desc create mode 100644 src/hidapi/windows/test/data/047F_C056_0001_000C_real.rpt_desc create mode 100644 src/hidapi/windows/test/data/047F_C056_0003_FFA0.pp_data create mode 100644 src/hidapi/windows/test/data/047F_C056_0003_FFA0_expected.rpt_desc create mode 100644 src/hidapi/windows/test/data/047F_C056_0003_FFA0_real.rpt_desc create mode 100644 src/hidapi/windows/test/data/047F_C056_0005_000B.pp_data create mode 100644 src/hidapi/windows/test/data/047F_C056_0005_000B_expected.rpt_desc create mode 100644 src/hidapi/windows/test/data/047F_C056_0005_000B_real.rpt_desc create mode 100644 src/hidapi/windows/test/data/17CC_1130_0000_FF01.pp_data create mode 100644 src/hidapi/windows/test/data/17CC_1130_0000_FF01_expected.rpt_desc create mode 100644 src/hidapi/windows/test/data/17CC_1130_0000_FF01_real.rpt_desc create mode 100644 src/hidapi/windows/test/hid_report_reconstructor_test.c diff --git a/src/hidapi/.appveyor.yml b/src/hidapi/.appveyor.yml new file mode 100644 index 000000000..210b3fa46 --- /dev/null +++ b/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" diff --git a/src/hidapi/.builds/freebsd.yml b/src/hidapi/.builds/freebsd.yml new file mode 100644 index 000000000..1679b03c3 --- /dev/null +++ b/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 diff --git a/src/hidapi/.builds/netbsd.yml b/src/hidapi/.builds/netbsd.yml new file mode 100644 index 000000000..413d91c02 --- /dev/null +++ b/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 diff --git a/src/hidapi/.builds/openbsd.yml b/src/hidapi/.builds/openbsd.yml new file mode 100644 index 000000000..780df7f66 --- /dev/null +++ b/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 diff --git a/src/hidapi/.cirrus.yml b/src/hidapi/.cirrus.yml new file mode 100644 index 000000000..b4cf20166 --- /dev/null +++ b/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 diff --git a/src/hidapi/.gitattributes b/src/hidapi/.gitattributes new file mode 100644 index 000000000..edb79febc --- /dev/null +++ b/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 diff --git a/src/hidapi/.github/workflows/builds.yml b/src/hidapi/.github/workflows/builds.yml new file mode 100644 index 000000000..056df2cc0 --- /dev/null +++ b/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/setup-msbuild@v1.1 + - name: MSBuild x86 + run: msbuild windows\hidapi.sln /p:Configuration=Release /p:Platform=Win32 + - name: Check artifacts x86 + uses: andstor/file-existence-action@v2 + with: + files: "windows/Release/hidapi.dll, windows/Release/hidapi.lib, windows/Release/hidapi.pdb" + fail: true + - name: MSBuild x64 + run: msbuild windows\hidapi.sln /p:Configuration=Release /p:Platform=x64 + - name: Check artifacts x64 + uses: andstor/file-existence-action@v2 + with: + files: "windows/x64/Release/hidapi.dll, windows/x64/Release/hidapi.lib, windows/x64/Release/hidapi.pdb" + fail: true + - name: Gather artifacts + run: | + md artifacts + md artifacts\x86 + md artifacts\x64 + md artifacts\include + Copy-Item "windows\Release\hidapi.dll","windows\Release\hidapi.lib","windows\Release\hidapi.pdb" -Destination "artifacts\x86" + Copy-Item "windows\x64\Release\hidapi.dll","windows\x64\Release\hidapi.lib","windows\x64\Release\hidapi.pdb" -Destination "artifacts\x64" + Copy-Item "hidapi\hidapi.h","windows\hidapi_winapi.h" -Destination "artifacts\include" + - name: Upload artifacts + uses: actions/upload-artifact@v3 + with: + name: hidapi-win + path: artifacts/ + retention-days: ${{ (github.event_name == 'pull_request' || github.ref_name != 'master') && 7 || 90 }} + + + fedora-mingw: + + runs-on: ubuntu-latest + container: fedora:latest + steps: + - uses: actions/checkout@v3 + with: + path: hidapisrc + - name: Install dependencies + run: sudo dnf install -y autoconf automake libtool mingw64-gcc cmake ninja-build make + - name: Configure CMake + run: | + rm -rf build install + mingw64-cmake -B build/shared-cmake -S hidapisrc -GNinja -DCMAKE_BUILD_TYPE=RelWithDebInfo -DCMAKE_INSTALL_PREFIX=install/shared-cmake -DHIDAPI_BUILD_HIDTEST=ON "-DCMAKE_C_FLAGS=${NIX_COMPILE_FLAGS}" + mingw64-cmake -B build/static-cmake -S hidapisrc -GNinja -DCMAKE_BUILD_TYPE=RelWithDebInfo -DCMAKE_INSTALL_PREFIX=install/static-cmake -DBUILD_SHARED_LIBS=FALSE -DHIDAPI_BUILD_HIDTEST=ON "-DCMAKE_C_FLAGS=${NIX_COMPILE_FLAGS}" + - name: Configure Automake + working-directory: hidapisrc + run: | + ./bootstrap + mingw64-configure + - name: Build CMake Shared + working-directory: build/shared-cmake + run: ninja install + - name: Build CMake Static + working-directory: build/static-cmake + run: ninja install + - name: Build Automake + working-directory: hidapisrc + run: | + make + make DESTDIR=$PWD/../install/automake install + make clean + - name: Build manual Makefile + working-directory: hidapisrc/windows + run: make -f Makefile-manual OS=MINGW CC=x86_64-w64-mingw32-gcc + - name: Check artifacts + uses: andstor/file-existence-action@v2 + with: + files: "install/shared-cmake/bin/libhidapi.dll, \ + install/shared-cmake/lib/libhidapi.dll.a, \ + install/shared-cmake/include/hidapi/hidapi.h, \ + install/shared-cmake/include/hidapi/hidapi_winapi.h, \ + install/static-cmake/lib/libhidapi.a, \ + install/static-cmake/include/hidapi/hidapi.h, \ + install/static-cmake/include/hidapi/hidapi_winapi.h" + fail: true + - name: Check CMake Export Package Shared + run: | + mingw64-cmake \ + -GNinja \ + -B build/shared_test \ + -S hidapisrc/hidtest \ + -Dhidapi_DIR=$PWD/install/shared-cmake/lib/cmake/hidapi \ + -DCMAKE_INSTALL_PREFIX=install/shared_test \ + "-DCMAKE_C_FLAGS=${NIX_COMPILE_FLAGS}" + cd build/shared_test + ninja install + - name: Check CMake Export Package Static + run: | + mingw64-cmake \ + -GNinja \ + -B build/static_test \ + -S hidapisrc/hidtest \ + -Dhidapi_DIR=$PWD/install/static-cmake/lib/cmake/hidapi \ + -DCMAKE_INSTALL_PREFIX=install/static_test \ + "-DCMAKE_C_FLAGS=${NIX_COMPILE_FLAGS}" + cd build/static_test + ninja install + + + archlinux: + + runs-on: ubuntu-latest + container: archlinux:latest + steps: + - uses: actions/checkout@v3 + with: + path: hidapisrc + - name: Install dependencies + run: | + pacman -Sy + pacman -S --noconfirm gcc pkg-config autoconf automake libtool libusb libudev0 cmake make + - name: Configure CMake + run: | + rm -rf build install + cmake -B build/shared-cmake -S hidapisrc -DCMAKE_BUILD_TYPE=RelWithDebInfo -DCMAKE_INSTALL_PREFIX=install/shared-cmake -DHIDAPI_BUILD_HIDTEST=ON "-DCMAKE_C_FLAGS=${NIX_COMPILE_FLAGS}" + cmake -B build/static-cmake -S hidapisrc -DCMAKE_BUILD_TYPE=RelWithDebInfo -DCMAKE_INSTALL_PREFIX=install/static-cmake -DBUILD_SHARED_LIBS=FALSE -DHIDAPI_BUILD_HIDTEST=ON "-DCMAKE_C_FLAGS=${NIX_COMPILE_FLAGS}" + - name: Configure Automake + working-directory: hidapisrc + run: | + ./bootstrap + ./configure + - name: Build CMake Shared + working-directory: build/shared-cmake + run: make install + - name: Build CMake Static + working-directory: build/static-cmake + run: make install + - name: Build Automake + working-directory: hidapisrc + run: | + make + make DESTDIR=$PWD/../install/automake install + make clean + - name: Build manual Makefile + run: | + cd hidapisrc/linux + make -f Makefile-manual + cd ../libusb + make -f Makefile-manual + - name: Check artifacts + uses: andstor/file-existence-action@v2 + with: + files: "install/shared-cmake/lib/libhidapi-libusb.so, \ + install/shared-cmake/lib/libhidapi-hidraw.so, \ + install/shared-cmake/include/hidapi/hidapi.h, \ + install/shared-cmake/include/hidapi/hidapi_libusb.h, \ + install/static-cmake/lib/libhidapi-libusb.a, \ + install/static-cmake/lib/libhidapi-hidraw.a, \ + install/static-cmake/include/hidapi/hidapi.h, \ + install/static-cmake/include/hidapi/hidapi_libusb.h" + fail: true + - name: Check CMake Export Package Shared + run: | + cmake \ + -B build/shared_test \ + -S hidapisrc/hidtest \ + -Dhidapi_ROOT=install/shared-cmake \ + -DCMAKE_INSTALL_PREFIX=install/shared_test \ + "-DCMAKE_C_FLAGS=${NIX_COMPILE_FLAGS}" + cd build/shared_test + make install + - name: Check CMake Export Package Static + run: | + cmake \ + -B build/static_test \ + -S hidapisrc/hidtest \ + -Dhidapi_ROOT=install/static-cmake \ + -DCMAKE_INSTALL_PREFIX=install/static_test \ + "-DCMAKE_C_FLAGS=${NIX_COMPILE_FLAGS}" + cd build/static_test + make install + + + alpine: + + runs-on: ubuntu-latest + container: alpine:edge + env: + # A bug in musl: https://www.openwall.com/lists/musl/2020/01/20/2 + ALPINE_COMPILE_FLAGS: ${NIX_COMPILE_FLAGS} -Wno-overflow + steps: + - uses: actions/checkout@v3 + with: + path: hidapisrc + - name: Install dependencies + run: | + apk add gcc musl-dev autoconf automake libtool eudev-dev libusb-dev linux-headers cmake ninja make + - name: Configure CMake + run: | + rm -rf build install + cmake -B build/shared-cmake -S hidapisrc -GNinja -DCMAKE_BUILD_TYPE=RelWithDebInfo -DCMAKE_INSTALL_PREFIX=install/shared-cmake -DHIDAPI_BUILD_HIDTEST=ON "-DCMAKE_C_FLAGS=${ALPINE_COMPILE_FLAGS}" + cmake -B build/static-cmake -S hidapisrc -GNinja -DCMAKE_BUILD_TYPE=RelWithDebInfo -DCMAKE_INSTALL_PREFIX=install/static-cmake -DBUILD_SHARED_LIBS=FALSE -DHIDAPI_BUILD_HIDTEST=ON "-DCMAKE_C_FLAGS=${ALPINE_COMPILE_FLAGS}" + - name: Configure Automake + working-directory: hidapisrc + run: | + ./bootstrap + ./configure + - name: Build CMake Shared + working-directory: build/shared-cmake + run: ninja install + - name: Build CMake Static + working-directory: build/static-cmake + run: ninja install + - name: Build Automake + working-directory: hidapisrc + run: | + make + make DESTDIR=$PWD/../install/automake install + make clean + - name: Build manual Makefile + run: | + cd hidapisrc/linux + make -f Makefile-manual + cd ../libusb + make -f Makefile-manual + - name: Check artifacts + uses: andstor/file-existence-action@v2 + with: + files: "install/shared-cmake/lib/libhidapi-libusb.so, \ + install/shared-cmake/lib/libhidapi-hidraw.so, \ + install/shared-cmake/include/hidapi/hidapi.h, \ + install/shared-cmake/include/hidapi/hidapi_libusb.h, \ + install/static-cmake/lib/libhidapi-libusb.a, \ + install/static-cmake/lib/libhidapi-hidraw.a, \ + install/static-cmake/include/hidapi/hidapi.h, \ + install/static-cmake/include/hidapi/hidapi_libusb.h" + fail: true + - name: Check CMake Export Package Shared + run: | + cmake \ + -GNinja \ + -B build/shared_test \ + -S hidapisrc/hidtest \ + -Dhidapi_ROOT=install/shared-cmake \ + -DCMAKE_INSTALL_PREFIX=install/shared_test \ + "-DCMAKE_C_FLAGS=${ALPINE_COMPILE_FLAGS}" + cd build/shared_test + ninja install + - name: Check CMake Export Package Static + run: | + cmake \ + -GNinja \ + -B build/static_test \ + -S hidapisrc/hidtest \ + -Dhidapi_ROOT=install/static-cmake \ + -DCMAKE_INSTALL_PREFIX=install/static_test \ + "-DCMAKE_C_FLAGS=${ALPINE_COMPILE_FLAGS}" + cd build/static_test + ninja install diff --git a/src/hidapi/.github/workflows/checks.yml b/src/hidapi/.github/workflows/checks.yml new file mode 100644 index 000000000..e03dc66ee --- /dev/null +++ b/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/setup-msvc-dev@v2.0.0 + with: + arch: x64 + - name: Configure + run: | + cmake -B build -S src -G"NMake Makefiles" -DCMAKE_BUILD_TYPE=RelWithDebInfo -DHIDAPI_WITH_TESTS=ON -DHIDAPI_BUILD_HIDTEST=ON + - name: Lookup Coverity Build Tool hash + id: coverity-cache-lookup + run: | + $coverity_hash=Invoke-RestMethod -Uri https://scan.coverity.com/download/cxx/win64 -Method Post -Body @{token='${{ secrets.COVERITY_SCAN_TOKEN }}';project='hidapi';md5=1} + echo "coverity_hash=$coverity_hash" >> $Env:GITHUB_OUTPUT + - name: Get cached Coverity Build Tool + id: cov-build-cache + uses: actions/cache@v3 + with: + path: cov-root + key: cov-root-cxx-win64-${{ steps.coverity-cache-lookup.outputs.coverity_hash }} + - name: Get and configure Coverity + if: steps.cov-build-cache.outputs.cache-hit != 'true' + run: | + Invoke-WebRequest -Uri https://scan.coverity.com/download/cxx/win64 -OutFile coverity.zip -Method Post -Body @{token='${{ secrets.COVERITY_SCAN_TOKEN }}';project='hidapi'} + Remove-Item 'cov-root' -Recurse -Force -ErrorAction SilentlyContinue + Expand-Archive coverity.zip -DestinationPath cov-root + + $cov_root=Get-ChildItem -Path 'cov-root' + $Env:PATH += ";$($cov_root.FullName)\bin" + cov-configure -msvc + - name: Make Coverity available in PATH + run: | + $cov_root=Get-ChildItem -Path 'cov-root' + echo "$($cov_root.FullName)\bin" >> $Env:GITHUB_PATH + - name: Build with Coverity + working-directory: build + run: | + cov-build --dir cov-int nmake + Rename-Item ".\cov-int\emit\$(hostname)" hostname + - name: Backup Coverity logs + uses: actions/upload-artifact@v3 + with: + name: coverity-logs-windows + path: build/cov-int + retention-days: 7 + + + coverity-macos: + runs-on: macos-latest + needs: [coverity-windows] + + steps: + - uses: actions/checkout@v3 + with: + path: src + - name: Install dependencies + run: brew install ninja + - name: Configure + run: | + cmake -B build -S src -GNinja -DCMAKE_BUILD_TYPE=RelWithDebInfo -DHIDAPI_WITH_TESTS=ON -DHIDAPI_BUILD_HIDTEST=ON -DCMAKE_C_COMPILER=clang + - uses: actions/download-artifact@v3 + with: + name: coverity-logs-windows + path: build/cov-int + - name: Fixup cov-int + run: | + rm -f build/cov-int/emit/hostname/emit-db.lock build/cov-int/emit/hostname/emit-db.write-lock + mv build/cov-int/emit/hostname build/cov-int/emit/$(hostname) + - name: Lookup Coverity Build Tool hash + id: coverity-cache-lookup + shell: bash + run: | + hash=$(curl https://scan.coverity.com/download/cxx/Darwin --data "token=${{ secrets.COVERITY_SCAN_TOKEN }}&project=hidapi&md5=1") + echo "coverity_hash=${hash}" >> $GITHUB_OUTPUT + - name: Get cached Coverity Build Tool + id: cov-build-cache + uses: actions/cache@v3 + with: + path: cov-root + key: cov-root-cxx-Darwin-${{ steps.coverity-cache-lookup.outputs.coverity_hash }} + - name: Get and configure Coverity + if: steps.cov-build-cache.outputs.cache-hit != 'true' + run: | + curl https://scan.coverity.com/download/cxx/Darwin --output coverity.dmg --data "token=${{ secrets.COVERITY_SCAN_TOKEN }}&project=hidapi" + hdiutil attach coverity.dmg -mountroot coverity + export COV_DIR_NAME=$(ls -1 --color=never coverity) + rm -rf cov-root + mkdir cov-root + cp ./coverity/${COV_DIR_NAME}/${COV_DIR_NAME}.sh cov-root/ + cd cov-root/ + ./${COV_DIR_NAME}.sh + ./bin/cov-configure --clang + - name: Make Coverity available in PATH + run: echo "$(pwd)/cov-root/bin" >> $GITHUB_PATH + - name: Build with Coverity + working-directory: build + run: | + cov-build --dir cov-int --append-log ninja + mv cov-int/emit/$(hostname) cov-int/emit/hostname + - name: Backup Coverity logs + uses: actions/upload-artifact@v3 + with: + name: coverity-logs-windows-macos + path: build/cov-int + retention-days: 7 + + + coverity-ubuntu: + runs-on: ubuntu-latest + needs: [coverity-macos] + + steps: + - uses: actions/checkout@v3 + with: + path: src + - name: Install dependencies + run: sudo apt install libudev-dev libusb-1.0-0-dev ninja-build + - name: Configure + run: | + cmake -B build -S src -GNinja -DCMAKE_BUILD_TYPE=RelWithDebInfo -DHIDAPI_WITH_TESTS=ON -DHIDAPI_BUILD_HIDTEST=ON -DCMAKE_C_COMPILER=gcc + - uses: actions/download-artifact@v3 + with: + name: coverity-logs-windows-macos + path: build/cov-int + - name: Fixup cov-int + run: | + rm -f build/cov-int/emit/hostname/emit-db.lock build/cov-int/emit/hostname/emit-db.write-lock + mv build/cov-int/emit/hostname build/cov-int/emit/$(hostname) + - name: Lookup Coverity Build Tool hash + id: coverity-cache-lookup + shell: bash + run: | + hash=$(curl https://scan.coverity.com/download/cxx/linux64 --data "token=${{ secrets.COVERITY_SCAN_TOKEN }}&project=hidapi&md5=1") + echo "coverity_hash=${hash}" >> $GITHUB_OUTPUT + - name: Get cached Coverity Build Tool + id: cov-build-cache + uses: actions/cache@v3 + with: + path: cov-root + key: cov-root-cxx-linux64-${{ steps.coverity-cache-lookup.outputs.coverity_hash }} + - name: Get and configure Coverity + if: steps.cov-build-cache.outputs.cache-hit != 'true' + run: | + curl https://scan.coverity.com/download/cxx/linux64 --output coverity.tar.gz --data "token=${{ secrets.COVERITY_SCAN_TOKEN }}&project=hidapi" + rm -rf cov-root + mkdir cov-root + tar -xzf coverity.tar.gz --strip 1 -C cov-root + ./cov-root/bin/cov-configure --gcc + - name: Make Coverity available in PATH + run: echo "$(pwd)/cov-root/bin" >> $GITHUB_PATH + - name: Build with Coverity + working-directory: build + run: | + cov-build --dir cov-int --append-log ninja + - name: Submit results to Coverity Scan + working-directory: build + run: | + tar -czf cov-int.tar.gz cov-int + curl --form token=${{ secrets.COVERITY_SCAN_TOKEN }} \ + --form email=${{ secrets.COVERITY_SCAN_EMAIL }} \ + --form file=@cov-int.tar.gz \ + --form version="$GITHUB_SHA" \ + --form description="Automatic HIDAPI build" \ + https://scan.coverity.com/builds?project=hidapi + mv cov-int/emit/$(hostname) cov-int/emit/hostname + - name: Backup Coverity logs + uses: actions/upload-artifact@v3 + with: + name: coverity-logs-windows-macos-linux + path: build/cov-int + retention-days: 7 diff --git a/src/hidapi/.github/workflows/docs.yaml b/src/hidapi/.github/workflows/docs.yaml new file mode 100644 index 000000000..4ec09a502 --- /dev/null +++ b/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 diff --git a/src/hidapi/.gitignore b/src/hidapi/.gitignore new file mode 100644 index 000000000..048900e28 --- /dev/null +++ b/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/ diff --git a/src/hidapi/AUTHORS.txt b/src/hidapi/AUTHORS.txt index f311978d3..7193d71e0 100644 --- a/src/hidapi/AUTHORS.txt +++ b/src/hidapi/AUTHORS.txt @@ -10,7 +10,9 @@ Ludovic Rousseau : 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 diff --git a/src/hidapi/BUILD.autotools.md b/src/hidapi/BUILD.autotools.md new file mode 100644 index 000000000..24b20a5af --- /dev/null +++ b/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 +``` diff --git a/src/hidapi/BUILD.cmake.md b/src/hidapi/BUILD.cmake.md new file mode 100644 index 000000000..9c51d0ce0 --- /dev/null +++ b/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 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 +# configure the build +cmake +# 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=value`. +E.g.: +```sh +# install command now would install things into /usr +cmake -DCMAKE_INSTALL_PREFIX=/usr +``` + +
+ Using a specific CMake generator + +An example of using `Ninja` as a CMake generator: + +```sh +cd +# configure the build +cmake -GNinja +# 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). + +

+ +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; + +
+ macOS-specific variables + + - [`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; + +

+ +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; + +
+ Linux-specific variables + + - `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. + +

+ +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;
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=`. + +_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 `` 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) +``` + +
+ NOTE + + 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. +

+ +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")
+E.g.2:
+![an example of drop-down menu in cmake-gui](documentation/cmake-gui-drop-down.png "cmake-gui drop-down menu")
+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 -B "/static" -DCMAKE_INSTALL_PREFIX= -DBUILD_SHARED_LIBS=FALSE +cmake --build "/static" +# Shared libraries +cmake -S -B "/shared" -DCMAKE_INSTALL_PREFIX= -DBUILD_SHARED_LIBS=TRUE +cmake --build "/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 "/static" +# Shared libraries +cmake --install "/shared" + +``` diff --git a/src/hidapi/BUILD.md b/src/hidapi/BUILD.md new file mode 100644 index 000000000..d7a3546f6 --- /dev/null +++ b/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).
+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.
+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 `/hid.c` and `hidapi/hidapi.h` is enough to build HIDAPI. +The only guarantee that `/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. diff --git a/src/hidapi/CMakeLists.txt b/src/hidapi/CMakeLists.txt new file mode 100644 index 000000000..e18ee23be --- /dev/null +++ b/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() diff --git a/src/hidapi/HACKING.txt b/src/hidapi/HACKING.txt index 761d4b655..e06b533aa 100644 --- a/src/hidapi/HACKING.txt +++ b/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-` +5. Push newly created tag: `git push origin hidapi-` +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. diff --git a/src/hidapi/Makefile.am b/src/hidapi/Makefile.am index 3382a1f04..00bcb73cf 100644 --- a/src/hidapi/Makefile.am +++ b/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 \ diff --git a/src/hidapi/README.md b/src/hidapi/README.md new file mode 100644 index 000000000..257b9f340 --- /dev/null +++ b/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.
+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 . 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 // printf +#include // wchar_t + +#include + +#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. diff --git a/src/hidapi/README.txt b/src/hidapi/README.txt deleted file mode 100644 index 756901ec5..000000000 --- a/src/hidapi/README.txt +++ /dev/null @@ -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 -#endif -#include -#include -#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 diff --git a/src/hidapi/VERSION b/src/hidapi/VERSION new file mode 100644 index 000000000..0548fb4e9 --- /dev/null +++ b/src/hidapi/VERSION @@ -0,0 +1 @@ +0.14.0 \ No newline at end of file diff --git a/src/hidapi/android/hid.cpp b/src/hidapi/android/hid.cpp deleted file mode 100644 index 450cab208..000000000 --- a/src/hidapi/android/hid.cpp +++ /dev/null @@ -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 -#include - -#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 -#include // For ETIMEDOUT and ECONNRESET -#include // 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 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 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 g_Devices; - -static hid_device_ref FindDevice( int nDeviceId ) -{ - hid_device_ref 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 pDevice( new CHIDDevice( nDeviceID, pInfo ) ); - - hid_mutex_guard l( &g_DevicesMutex ); - hid_device_ref 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 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 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 pDevice; - { - hid_mutex_guard l( &g_DevicesMutex ); - hid_device_ref 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 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 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 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 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 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 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 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 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 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 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 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 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) } -}; diff --git a/src/hidapi/android/hid.h b/src/hidapi/android/hid.h deleted file mode 100644 index 5e6253bf8..000000000 --- a/src/hidapi/android/hid.h +++ /dev/null @@ -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 - -#ifdef __cplusplus -extern "C" { -#endif - -extern JNINativeMethod HIDDeviceManager_tab[8]; - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/src/hidapi/android/jni/Android.mk b/src/hidapi/android/jni/Android.mk deleted file mode 100644 index 4462e88bf..000000000 --- a/src/hidapi/android/jni/Android.mk +++ /dev/null @@ -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) diff --git a/src/hidapi/android/jni/Application.mk b/src/hidapi/android/jni/Application.mk deleted file mode 100644 index 4fc6ba506..000000000 --- a/src/hidapi/android/jni/Application.mk +++ /dev/null @@ -1,2 +0,0 @@ -APP_STL := gnustl_static -APP_ABI := armeabi-v7a diff --git a/src/hidapi/android/project.properties b/src/hidapi/android/project.properties deleted file mode 100644 index 6e18427a4..000000000 --- a/src/hidapi/android/project.properties +++ /dev/null @@ -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 diff --git a/src/hidapi/configure.ac b/src/hidapi/configure.ac index c6747f906..cc7ceaca0 100644 --- a/src/hidapi/configure.ac +++ b/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],[alan@signal11.us]) +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]) diff --git a/src/hidapi/dist/hidapi.podspec b/src/hidapi/dist/hidapi.podspec new file mode 100644 index 000000000..74642ef63 --- /dev/null +++ b/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" => "alan@signal11.us", + "Ludovic Rousseau" => "rousseau@debian.org", + "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 diff --git a/src/hidapi/documentation/cmake-gui-drop-down.png b/src/hidapi/documentation/cmake-gui-drop-down.png new file mode 100644 index 0000000000000000000000000000000000000000..548abe8c1ea29d6a28fd94c1f29da043ded5b43c GIT binary patch literal 22316 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 LKetww$gF literal 0 HcmV?d00001 diff --git a/src/hidapi/documentation/cmake-gui-highlights.png b/src/hidapi/documentation/cmake-gui-highlights.png new file mode 100644 index 0000000000000000000000000000000000000000..228838ff7cf8449e4c0fa1f47c12e297d9039eca GIT binary patch literal 77327 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
aH8SwR7+bBRCtK#&Zx~(NZO^`an&0Q3^fq8fCk3 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