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 0000000000..210b3fa463 --- /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 0000000000..1679b03c38 --- /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 0000000000..413d91c020 --- /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 0000000000..780df7f661 --- /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 0000000000..b4cf201667 --- /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 0000000000..edb79febc5 --- /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 0000000000..056df2cc08 --- /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 0000000000..e03dc66ee2 --- /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 0000000000..4ec09a502e --- /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 0000000000..048900e287 --- /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 f311978d33..7193d71e0b 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 0000000000..24b20a5af2 --- /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 0000000000..9c51d0ce0b --- /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 0000000000..d7a3546f6a --- /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 0000000000..e18ee23be8 --- /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 761d4b6550..e06b533aa0 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 3382a1f040..00bcb73cf4 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 0000000000..257b9f340a --- /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 756901ec52..0000000000 --- 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 0000000000..0548fb4e9b --- /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 450cab2084..0000000000 --- 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 5e6253bf8c..0000000000 --- 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 4462e88bf3..0000000000 --- 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 4fc6ba5064..0000000000 --- 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 6e18427a42..0000000000 --- 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 c6747f906a..cc7ceaca00 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 0000000000..74642ef632 --- /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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 literal 0 HcmV?d00001 diff --git a/src/hidapi/doxygen/Doxyfile b/src/hidapi/doxygen/Doxyfile index 9d983e9f27..4e01360e09 100644 --- a/src/hidapi/doxygen/Doxyfile +++ b/src/hidapi/doxygen/Doxyfile @@ -1,96 +1,151 @@ -# Doxyfile 1.7.1 +# Doxyfile 1.9.6 # This file describes the settings to be used by the documentation system -# doxygen (www.doxygen.org) for a project +# doxygen (www.doxygen.org) for a project. # -# All text after a hash (#) is considered a comment and will be ignored +# All text after a double hash (##) is considered a comment and is placed in +# front of the TAG it is preceding. +# +# All text after a single hash (#) is considered a comment and will be ignored. # The format is: -# TAG = value [value, ...] -# For lists items can also be appended using: -# TAG += value [value, ...] -# Values that contain spaces should be placed between quotes (" ") +# TAG = value [value, ...] +# For lists, items can also be appended using: +# TAG += value [value, ...] +# Values that contain spaces should be placed between quotes (\" \"). +# +# Note: +# +# Use doxygen to compare the used configuration file with the template +# configuration file: +# doxygen -x [configFile] +# Use doxygen to compare the used configuration file with the template +# configuration file without replacing the environment variables or CMake type +# replacement variables: +# doxygen -x_noenv [configFile] #--------------------------------------------------------------------------- # Project related configuration options #--------------------------------------------------------------------------- -# This tag specifies the encoding used for all characters in the config file -# that follow. The default is UTF-8 which is also the encoding used for all +# This tag specifies the encoding used for all characters in the configuration +# file that follow. The default is UTF-8 which is also the encoding used for all # text before the first occurrence of this tag. Doxygen uses libiconv (or the # iconv built into libc) for the transcoding. See -# http://www.gnu.org/software/libiconv for the list of possible encodings. +# https://www.gnu.org/software/libiconv/ for the list of possible encodings. +# The default value is: UTF-8. DOXYFILE_ENCODING = UTF-8 -# The PROJECT_NAME tag is a single word (or a sequence of words surrounded -# by quotes) that should identify the project. +# The PROJECT_NAME tag is a single word (or a sequence of words surrounded by +# double-quotes, unless you are using Doxywizard) that should identify the +# project for which the documentation is generated. This name is used in the +# title of most generated pages and in a few other places. +# The default value is: My Project. PROJECT_NAME = hidapi -# The PROJECT_NUMBER tag can be used to enter a project or revision number. -# This could be handy for archiving the generated documentation or -# if some version control system is used. +# The PROJECT_NUMBER tag can be used to enter a project or revision number. This +# could be handy for archiving the generated documentation or if some version +# control system is used. PROJECT_NUMBER = -# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) -# base path where the generated documentation will be put. -# If a relative path is entered, it will be relative to the location -# where doxygen was started. If left blank the current directory will be used. +# Using the PROJECT_BRIEF tag one can provide an optional one line description +# for a project that appears at the top of each page and should give viewer a +# quick idea about the purpose of the project. Keep the description short. + +PROJECT_BRIEF = + +# With the PROJECT_LOGO tag one can specify a logo or an icon that is included +# in the documentation. The maximum height of the logo should not exceed 55 +# pixels and the maximum width should not exceed 200 pixels. Doxygen will copy +# the logo to the output directory. + +PROJECT_LOGO = + +# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path +# into which the generated documentation will be written. If a relative path is +# entered, it will be relative to the location where doxygen was started. If +# left blank the current directory will be used. OUTPUT_DIRECTORY = -# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create -# 4096 sub-directories (in 2 levels) under the output directory of each output -# format and will distribute the generated files over these directories. -# Enabling this option can be useful when feeding doxygen a huge amount of -# source files, where putting all generated files in the same directory would -# otherwise cause performance problems for the file system. +# If the CREATE_SUBDIRS tag is set to YES then doxygen will create up to 4096 +# sub-directories (in 2 levels) under the output directory of each output format +# and will distribute the generated files over these directories. Enabling this +# option can be useful when feeding doxygen a huge amount of source files, where +# putting all generated files in the same directory would otherwise causes +# performance problems for the file system. Adapt CREATE_SUBDIRS_LEVEL to +# control the number of sub-directories. +# The default value is: NO. CREATE_SUBDIRS = NO +# Controls the number of sub-directories that will be created when +# CREATE_SUBDIRS tag is set to YES. Level 0 represents 16 directories, and every +# level increment doubles the number of directories, resulting in 4096 +# directories at level 8 which is the default and also the maximum value. The +# sub-directories are organized in 2 levels, the first level always has a fixed +# number of 16 directories. +# Minimum value: 0, maximum value: 8, default value: 8. +# This tag requires that the tag CREATE_SUBDIRS is set to YES. + +CREATE_SUBDIRS_LEVEL = 8 + +# If the ALLOW_UNICODE_NAMES tag is set to YES, doxygen will allow non-ASCII +# characters to appear in the names of generated files. If set to NO, non-ASCII +# characters will be escaped, for example _xE3_x81_x84 will be used for Unicode +# U+3044. +# The default value is: NO. + +ALLOW_UNICODE_NAMES = NO + # The OUTPUT_LANGUAGE tag is used to specify the language in which all # documentation generated by doxygen is written. Doxygen will use this # information to generate all constant output in the proper language. -# The default language is English, other supported languages are: -# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional, -# Croatian, Czech, Danish, Dutch, Esperanto, Farsi, Finnish, French, German, -# Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English -# messages), Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian, -# Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrilic, Slovak, -# Slovene, Spanish, Swedish, Ukrainian, and Vietnamese. +# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Bulgarian, +# Catalan, Chinese, Chinese-Traditional, Croatian, Czech, Danish, Dutch, English +# (United States), Esperanto, Farsi (Persian), Finnish, French, German, Greek, +# Hindi, Hungarian, Indonesian, Italian, Japanese, Japanese-en (Japanese with +# English messages), Korean, Korean-en (Korean with English messages), Latvian, +# Lithuanian, Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, +# Romanian, Russian, Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, +# Swedish, Turkish, Ukrainian and Vietnamese. +# The default value is: English. OUTPUT_LANGUAGE = English -# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will -# include brief member descriptions after the members that are listed in -# the file and class documentation (similar to JavaDoc). -# Set to NO to disable this. +# If the BRIEF_MEMBER_DESC tag is set to YES, doxygen will include brief member +# descriptions after the members that are listed in the file and class +# documentation (similar to Javadoc). Set to NO to disable this. +# The default value is: YES. BRIEF_MEMBER_DESC = YES -# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend -# the brief description of a member or function before the detailed description. -# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the +# If the REPEAT_BRIEF tag is set to YES, doxygen will prepend the brief +# description of a member or function before the detailed description +# +# Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the # brief descriptions will be completely suppressed. +# The default value is: YES. REPEAT_BRIEF = YES -# This tag implements a quasi-intelligent brief description abbreviator -# that is used to form the text in various listings. Each string -# in this list, if found as the leading text of the brief description, will be -# stripped from the text and the result after processing the whole list, is -# used as the annotated text. Otherwise, the brief description is used as-is. -# If left blank, the following values are used ("$name" is automatically -# replaced with the name of the entity): "The $name class" "The $name widget" -# "The $name file" "is" "provides" "specifies" "contains" -# "represents" "a" "an" "the" +# This tag implements a quasi-intelligent brief description abbreviator that is +# used to form the text in various listings. Each string in this list, if found +# as the leading text of the brief description, will be stripped from the text +# and the result, after processing the whole list, is used as the annotated +# text. Otherwise, the brief description is used as-is. If left blank, the +# following values are used ($name is automatically replaced with the name of +# the entity):The $name class, The $name widget, The $name file, is, provides, +# specifies, contains, represents, a, an and the. ABBREVIATE_BRIEF = # If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then -# Doxygen will generate a detailed section even if there is only a brief +# doxygen will generate a detailed section even if there is only a brief # description. +# The default value is: NO. ALWAYS_DETAILED_SEC = NO @@ -98,531 +153,831 @@ ALWAYS_DETAILED_SEC = NO # inherited members of a class in the documentation of that class as if those # members were ordinary class members. Constructors, destructors and assignment # operators of the base classes will not be shown. +# The default value is: NO. INLINE_INHERITED_MEMB = NO -# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full -# path before files name in the file list and in the header files. If set -# to NO the shortest path that makes the file name unique will be used. +# If the FULL_PATH_NAMES tag is set to YES, doxygen will prepend the full path +# before files name in the file list and in the header files. If set to NO the +# shortest path that makes the file name unique will be used +# The default value is: YES. FULL_PATH_NAMES = YES -# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag -# can be used to strip a user-defined part of the path. Stripping is -# only done if one of the specified strings matches the left-hand part of -# the path. The tag can be used to show relative paths in the file list. -# If left blank the directory from which doxygen is run is used as the -# path to strip. +# The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path. +# Stripping is only done if one of the specified strings matches the left-hand +# part of the path. The tag can be used to show relative paths in the file list. +# If left blank the directory from which doxygen is run is used as the path to +# strip. +# +# Note that you can specify absolute paths here, but also relative paths, which +# will be relative from the directory where doxygen is started. +# This tag requires that the tag FULL_PATH_NAMES is set to YES. -STRIP_FROM_PATH = +STRIP_FROM_PATH = ../ -# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of -# the path mentioned in the documentation of a class, which tells -# the reader which header file to include in order to use a class. -# If left blank only the name of the header file containing the class -# definition is used. Otherwise one should specify the include paths that -# are normally passed to the compiler using the -I flag. +# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the +# path mentioned in the documentation of a class, which tells the reader which +# header file to include in order to use a class. If left blank only the name of +# the header file containing the class definition is used. Otherwise one should +# specify the list of include paths that are normally passed to the compiler +# using the -I flag. STRIP_FROM_INC_PATH = -# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter -# (but less readable) file names. This can be useful is your file systems -# doesn't support long names like on DOS, Mac, or CD-ROM. +# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but +# less readable) file names. This can be useful is your file systems doesn't +# support long names like on DOS, Mac, or CD-ROM. +# The default value is: NO. SHORT_NAMES = NO -# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen -# will interpret the first line (until the first dot) of a JavaDoc-style -# comment as the brief description. If set to NO, the JavaDoc -# comments will behave just like regular Qt-style comments -# (thus requiring an explicit @brief command for a brief description.) +# If the JAVADOC_AUTOBRIEF tag is set to YES then doxygen will interpret the +# first line (until the first dot) of a Javadoc-style comment as the brief +# description. If set to NO, the Javadoc-style will behave just like regular Qt- +# style comments (thus requiring an explicit @brief command for a brief +# description.) +# The default value is: NO. JAVADOC_AUTOBRIEF = NO -# If the QT_AUTOBRIEF tag is set to YES then Doxygen will -# interpret the first line (until the first dot) of a Qt-style -# comment as the brief description. If set to NO, the comments -# will behave just like regular Qt-style comments (thus requiring -# an explicit \brief command for a brief description.) +# If the JAVADOC_BANNER tag is set to YES then doxygen will interpret a line +# such as +# /*************** +# as being the beginning of a Javadoc-style comment "banner". If set to NO, the +# Javadoc-style will behave just like regular comments and it will not be +# interpreted by doxygen. +# The default value is: NO. + +JAVADOC_BANNER = NO + +# If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first +# line (until the first dot) of a Qt-style comment as the brief description. If +# set to NO, the Qt-style will behave just like regular Qt-style comments (thus +# requiring an explicit \brief command for a brief description.) +# The default value is: NO. QT_AUTOBRIEF = NO -# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen -# treat a multi-line C++ special comment block (i.e. a block of //! or /// -# comments) as a brief description. This used to be the default behaviour. -# The new default is to treat a multi-line C++ comment block as a detailed -# description. Set this tag to YES if you prefer the old behaviour instead. +# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make doxygen treat a +# multi-line C++ special comment block (i.e. a block of //! or /// comments) as +# a brief description. This used to be the default behavior. The new default is +# to treat a multi-line C++ comment block as a detailed description. Set this +# tag to YES if you prefer the old behavior instead. +# +# Note that setting this tag to YES also means that rational rose comments are +# not recognized any more. +# The default value is: NO. MULTILINE_CPP_IS_BRIEF = NO -# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented -# member inherits the documentation from any documented member that it -# re-implements. +# By default Python docstrings are displayed as preformatted text and doxygen's +# special commands cannot be used. By setting PYTHON_DOCSTRING to NO the +# doxygen's special commands can be used and the contents of the docstring +# documentation blocks is shown as doxygen documentation. +# The default value is: YES. + +PYTHON_DOCSTRING = YES + +# If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the +# documentation from any documented member that it re-implements. +# The default value is: YES. INHERIT_DOCS = YES -# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce -# a new page for each member. If set to NO, the documentation of a member will -# be part of the file/class/namespace that contains it. +# If the SEPARATE_MEMBER_PAGES tag is set to YES then doxygen will produce a new +# page for each member. If set to NO, the documentation of a member will be part +# of the file/class/namespace that contains it. +# The default value is: NO. SEPARATE_MEMBER_PAGES = NO -# The TAB_SIZE tag can be used to set the number of spaces in a tab. -# Doxygen uses this value to replace tabs by spaces in code fragments. +# The TAB_SIZE tag can be used to set the number of spaces in a tab. Doxygen +# uses this value to replace tabs by spaces in code fragments. +# Minimum value: 1, maximum value: 16, default value: 4. TAB_SIZE = 8 -# This tag can be used to specify a number of aliases that acts -# as commands in the documentation. An alias has the form "name=value". -# For example adding "sideeffect=\par Side Effects:\n" will allow you to -# put the command \sideeffect (or @sideeffect) in the documentation, which -# will result in a user-defined paragraph with heading "Side Effects:". -# You can put \n's in the value part of an alias to insert newlines. +# This tag can be used to specify a number of aliases that act as commands in +# the documentation. An alias has the form: +# name=value +# For example adding +# "sideeffect=@par Side Effects:^^" +# will allow you to put the command \sideeffect (or @sideeffect) in the +# documentation, which will result in a user-defined paragraph with heading +# "Side Effects:". Note that you cannot put \n's in the value part of an alias +# to insert newlines (in the resulting output). You can put ^^ in the value part +# of an alias to insert a newline as if a physical newline was in the original +# file. When you need a literal { or } or , in the value part of an alias you +# have to escape them by means of a backslash (\), this can lead to conflicts +# with the commands \{ and \} for these it is advised to use the version @{ and +# @} or use a double escape (\\{ and \\}) ALIASES = -# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C -# sources only. Doxygen will then generate output that is more tailored for C. -# For instance, some of the names that are used will be different. The list -# of all members will be omitted, etc. +# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources +# only. Doxygen will then generate output that is more tailored for C. For +# instance, some of the names that are used will be different. The list of all +# members will be omitted, etc. +# The default value is: NO. OPTIMIZE_OUTPUT_FOR_C = YES -# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java -# sources only. Doxygen will then generate output that is more tailored for -# Java. For instance, namespaces will be presented as packages, qualified -# scopes will look different, etc. +# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or +# Python sources only. Doxygen will then generate output that is more tailored +# for that language. For instance, namespaces will be presented as packages, +# qualified scopes will look different, etc. +# The default value is: NO. OPTIMIZE_OUTPUT_JAVA = NO # Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran -# sources only. Doxygen will then generate output that is more tailored for -# Fortran. +# sources. Doxygen will then generate output that is tailored for Fortran. +# The default value is: NO. OPTIMIZE_FOR_FORTRAN = NO # Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL -# sources. Doxygen will then generate output that is tailored for -# VHDL. +# sources. Doxygen will then generate output that is tailored for VHDL. +# The default value is: NO. OPTIMIZE_OUTPUT_VHDL = NO +# Set the OPTIMIZE_OUTPUT_SLICE tag to YES if your project consists of Slice +# sources only. Doxygen will then generate output that is more tailored for that +# language. For instance, namespaces will be presented as modules, types will be +# separated into more groups, etc. +# The default value is: NO. + +OPTIMIZE_OUTPUT_SLICE = NO + # Doxygen selects the parser to use depending on the extension of the files it -# parses. With this tag you can assign which parser to use for a given extension. -# Doxygen has a built-in mapping, but you can override or extend it using this -# tag. The format is ext=language, where ext is a file extension, and language -# is one of the parsers supported by doxygen: IDL, Java, Javascript, CSharp, C, -# C++, D, PHP, Objective-C, Python, Fortran, VHDL, C, C++. For instance to make -# doxygen treat .inc files as Fortran files (default is PHP), and .f files as C -# (default is Fortran), use: inc=Fortran f=C. Note that for custom extensions -# you also need to set FILE_PATTERNS otherwise the files are not read by doxygen. +# parses. With this tag you can assign which parser to use for a given +# extension. Doxygen has a built-in mapping, but you can override or extend it +# using this tag. The format is ext=language, where ext is a file extension, and +# language is one of the parsers supported by doxygen: IDL, Java, JavaScript, +# Csharp (C#), C, C++, Lex, D, PHP, md (Markdown), Objective-C, Python, Slice, +# VHDL, Fortran (fixed format Fortran: FortranFixed, free formatted Fortran: +# FortranFree, unknown formatted Fortran: Fortran. In the later case the parser +# tries to guess whether the code is fixed or free formatted code, this is the +# default for Fortran type files). For instance to make doxygen treat .inc files +# as Fortran files (default is PHP), and .f files as C (default is Fortran), +# use: inc=Fortran f=C. +# +# Note: For files without extension you can use no_extension as a placeholder. +# +# Note that for custom extensions you also need to set FILE_PATTERNS otherwise +# the files are not read by doxygen. When specifying no_extension you should add +# * to the FILE_PATTERNS. +# +# Note see also the list of default file extension mappings. EXTENSION_MAPPING = +# If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments +# according to the Markdown format, which allows for more readable +# documentation. See https://daringfireball.net/projects/markdown/ for details. +# The output of markdown processing is further processed by doxygen, so you can +# mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in +# case of backward compatibilities issues. +# The default value is: YES. + +MARKDOWN_SUPPORT = NO + +# When the TOC_INCLUDE_HEADINGS tag is set to a non-zero value, all headings up +# to that level are automatically included in the table of contents, even if +# they do not have an id attribute. +# Note: This feature currently applies only to Markdown headings. +# Minimum value: 0, maximum value: 99, default value: 5. +# This tag requires that the tag MARKDOWN_SUPPORT is set to YES. + +TOC_INCLUDE_HEADINGS = 5 + +# When enabled doxygen tries to link words that correspond to documented +# classes, or namespaces to their corresponding documentation. Such a link can +# be prevented in individual cases by putting a % sign in front of the word or +# globally by setting AUTOLINK_SUPPORT to NO. +# The default value is: YES. + +AUTOLINK_SUPPORT = YES + # If you use STL classes (i.e. std::string, std::vector, etc.) but do not want -# to include (a tag file for) the STL sources as input, then you should -# set this tag to YES in order to let doxygen match functions declarations and -# definitions whose arguments contain STL classes (e.g. func(std::string); v.s. -# func(std::string) {}). This also make the inheritance and collaboration +# to include (a tag file for) the STL sources as input, then you should set this +# tag to YES in order to let doxygen match functions declarations and +# definitions whose arguments contain STL classes (e.g. func(std::string); +# versus func(std::string) {}). This also make the inheritance and collaboration # diagrams that involve STL classes more complete and accurate. +# The default value is: NO. BUILTIN_STL_SUPPORT = NO # If you use Microsoft's C++/CLI language, you should set this option to YES to # enable parsing support. +# The default value is: NO. CPP_CLI_SUPPORT = NO -# Set the SIP_SUPPORT tag to YES if your project consists of sip sources only. -# Doxygen will parse them like normal C++ but will assume all classes use public -# instead of private inheritance when no explicit protection keyword is present. +# Set the SIP_SUPPORT tag to YES if your project consists of sip (see: +# https://www.riverbankcomputing.com/software/sip/intro) sources only. Doxygen +# will parse them like normal C++ but will assume all classes use public instead +# of private inheritance when no explicit protection keyword is present. +# The default value is: NO. SIP_SUPPORT = NO -# For Microsoft's IDL there are propget and propput attributes to indicate getter -# and setter methods for a property. Setting this option to YES (the default) -# will make doxygen to replace the get and set methods by a property in the -# documentation. This will only work if the methods are indeed getting or -# setting a simple type. If this is not the case, or you want to show the -# methods anyway, you should set this option to NO. +# For Microsoft's IDL there are propget and propput attributes to indicate +# getter and setter methods for a property. Setting this option to YES will make +# doxygen to replace the get and set methods by a property in the documentation. +# This will only work if the methods are indeed getting or setting a simple +# type. If this is not the case, or you want to show the methods anyway, you +# should set this option to NO. +# The default value is: YES. IDL_PROPERTY_SUPPORT = YES # If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC -# tag is set to YES, then doxygen will reuse the documentation of the first +# tag is set to YES then doxygen will reuse the documentation of the first # member in the group (if any) for the other members of the group. By default # all members of a group must be documented explicitly. +# The default value is: NO. DISTRIBUTE_GROUP_DOC = NO -# Set the SUBGROUPING tag to YES (the default) to allow class member groups of -# the same type (for instance a group of public functions) to be put as a -# subgroup of that type (e.g. under the Public Functions section). Set it to -# NO to prevent subgrouping. Alternatively, this can be done per class using -# the \nosubgrouping command. +# If one adds a struct or class to a group and this option is enabled, then also +# any nested class or struct is added to the same group. By default this option +# is disabled and one has to add nested compounds explicitly via \ingroup. +# The default value is: NO. + +GROUP_NESTED_COMPOUNDS = NO + +# Set the SUBGROUPING tag to YES to allow class member groups of the same type +# (for instance a group of public functions) to be put as a subgroup of that +# type (e.g. under the Public Functions section). Set it to NO to prevent +# subgrouping. Alternatively, this can be done per class using the +# \nosubgrouping command. +# The default value is: YES. SUBGROUPING = YES -# When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum -# is documented as struct, union, or enum with the name of the typedef. So +# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and unions +# are shown inside the group in which they are included (e.g. using \ingroup) +# instead of on a separate page (for HTML and Man pages) or section (for LaTeX +# and RTF). +# +# Note that this feature does not work in combination with +# SEPARATE_MEMBER_PAGES. +# The default value is: NO. + +INLINE_GROUPED_CLASSES = NO + +# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions +# with only public data fields or simple typedef fields will be shown inline in +# the documentation of the scope in which they are defined (i.e. file, +# namespace, or group documentation), provided this scope is documented. If set +# to NO, structs, classes, and unions are shown on a separate page (for HTML and +# Man pages) or section (for LaTeX and RTF). +# The default value is: NO. + +INLINE_SIMPLE_STRUCTS = NO + +# When TYPEDEF_HIDES_STRUCT tag is enabled, a typedef of a struct, union, or +# enum is documented as struct, union, or enum with the name of the typedef. So # typedef struct TypeS {} TypeT, will appear in the documentation as a struct # with name TypeT. When disabled the typedef will appear as a member of a file, -# namespace, or class. And the struct will be named TypeS. This can typically -# be useful for C code in case the coding convention dictates that all compound +# namespace, or class. And the struct will be named TypeS. This can typically be +# useful for C code in case the coding convention dictates that all compound # types are typedef'ed and only the typedef is referenced, never the tag name. +# The default value is: NO. TYPEDEF_HIDES_STRUCT = NO -# The SYMBOL_CACHE_SIZE determines the size of the internal cache use to -# determine which symbols to keep in memory and which to flush to disk. -# When the cache is full, less often used symbols will be written to disk. -# For small to medium size projects (<1000 input files) the default value is -# probably good enough. For larger projects a too small cache size can cause -# doxygen to be busy swapping symbols to and from disk most of the time -# causing a significant performance penality. -# If the system has enough physical memory increasing the cache will improve the -# performance by keeping more symbols in memory. Note that the value works on -# a logarithmic scale so increasing the size by one will rougly double the -# memory usage. The cache size is given by this formula: -# 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0, -# corresponding to a cache size of 2^16 = 65536 symbols +# The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This +# cache is used to resolve symbols given their name and scope. Since this can be +# an expensive process and often the same symbol appears multiple times in the +# code, doxygen keeps a cache of pre-resolved symbols. If the cache is too small +# doxygen will become slower. If the cache is too large, memory is wasted. The +# cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range +# is 0..9, the default is 0, corresponding to a cache size of 2^16=65536 +# symbols. At the end of a run doxygen will report the cache usage and suggest +# the optimal cache size from a speed point of view. +# Minimum value: 0, maximum value: 9, default value: 0. -SYMBOL_CACHE_SIZE = 0 +LOOKUP_CACHE_SIZE = 0 + +# The NUM_PROC_THREADS specifies the number of threads doxygen is allowed to use +# during processing. When set to 0 doxygen will based this on the number of +# cores available in the system. You can set it explicitly to a value larger +# than 0 to get more control over the balance between CPU load and processing +# speed. At this moment only the input processing can be done using multiple +# threads. Since this is still an experimental feature the default is set to 1, +# which effectively disables parallel processing. Please report any issues you +# encounter. Generating dot graphs in parallel is controlled by the +# DOT_NUM_THREADS setting. +# Minimum value: 0, maximum value: 32, default value: 1. + +NUM_PROC_THREADS = 1 #--------------------------------------------------------------------------- # Build related configuration options #--------------------------------------------------------------------------- -# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in -# documentation are documented, even if no documentation was available. -# Private class members and static file members will be hidden unless -# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES +# If the EXTRACT_ALL tag is set to YES, doxygen will assume all entities in +# documentation are documented, even if no documentation was available. Private +# class members and static file members will be hidden unless the +# EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES. +# Note: This will also disable the warnings about undocumented members that are +# normally produced when WARNINGS is set to YES. +# The default value is: NO. EXTRACT_ALL = NO -# If the EXTRACT_PRIVATE tag is set to YES all private members of a class -# will be included in the documentation. +# If the EXTRACT_PRIVATE tag is set to YES, all private members of a class will +# be included in the documentation. +# The default value is: NO. EXTRACT_PRIVATE = NO -# If the EXTRACT_STATIC tag is set to YES all static members of a file -# will be included in the documentation. +# If the EXTRACT_PRIV_VIRTUAL tag is set to YES, documented private virtual +# methods of a class will be included in the documentation. +# The default value is: NO. + +EXTRACT_PRIV_VIRTUAL = NO + +# If the EXTRACT_PACKAGE tag is set to YES, all members with package or internal +# scope will be included in the documentation. +# The default value is: NO. + +EXTRACT_PACKAGE = NO + +# If the EXTRACT_STATIC tag is set to YES, all static members of a file will be +# included in the documentation. +# The default value is: NO. EXTRACT_STATIC = NO -# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) -# defined locally in source files will be included in the documentation. -# If set to NO only classes defined in header files are included. +# If the EXTRACT_LOCAL_CLASSES tag is set to YES, classes (and structs) defined +# locally in source files will be included in the documentation. If set to NO, +# only classes defined in header files are included. Does not have any effect +# for Java sources. +# The default value is: YES. EXTRACT_LOCAL_CLASSES = YES -# This flag is only useful for Objective-C code. When set to YES local -# methods, which are defined in the implementation section but not in -# the interface are included in the documentation. -# If set to NO (the default) only methods in the interface are included. +# This flag is only useful for Objective-C code. If set to YES, local methods, +# which are defined in the implementation section but not in the interface are +# included in the documentation. If set to NO, only methods in the interface are +# included. +# The default value is: NO. EXTRACT_LOCAL_METHODS = NO # If this flag is set to YES, the members of anonymous namespaces will be # extracted and appear in the documentation as a namespace called -# 'anonymous_namespace{file}', where file will be replaced with the base -# name of the file that contains the anonymous namespace. By default -# anonymous namespace are hidden. +# 'anonymous_namespace{file}', where file will be replaced with the base name of +# the file that contains the anonymous namespace. By default anonymous namespace +# are hidden. +# The default value is: NO. EXTRACT_ANON_NSPACES = NO -# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all -# undocumented members of documented classes, files or namespaces. -# If set to NO (the default) these members will be included in the -# various overviews, but no documentation section is generated. -# This option has no effect if EXTRACT_ALL is enabled. +# If this flag is set to YES, the name of an unnamed parameter in a declaration +# will be determined by the corresponding definition. By default unnamed +# parameters remain unnamed in the output. +# The default value is: YES. + +RESOLVE_UNNAMED_PARAMS = YES + +# If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all +# undocumented members inside documented classes or files. If set to NO these +# members will be included in the various overviews, but no documentation +# section is generated. This option has no effect if EXTRACT_ALL is enabled. +# The default value is: NO. HIDE_UNDOC_MEMBERS = NO -# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all -# undocumented classes that are normally visible in the class hierarchy. -# If set to NO (the default) these classes will be included in the various -# overviews. This option has no effect if EXTRACT_ALL is enabled. +# If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all +# undocumented classes that are normally visible in the class hierarchy. If set +# to NO, these classes will be included in the various overviews. This option +# will also hide undocumented C++ concepts if enabled. This option has no effect +# if EXTRACT_ALL is enabled. +# The default value is: NO. HIDE_UNDOC_CLASSES = NO -# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all -# friend (class|struct|union) declarations. -# If set to NO (the default) these declarations will be included in the +# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend +# declarations. If set to NO, these declarations will be included in the # documentation. +# The default value is: NO. HIDE_FRIEND_COMPOUNDS = NO -# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any -# documentation blocks found inside the body of a function. -# If set to NO (the default) these blocks will be appended to the -# function's detailed documentation block. +# If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any +# documentation blocks found inside the body of a function. If set to NO, these +# blocks will be appended to the function's detailed documentation block. +# The default value is: NO. HIDE_IN_BODY_DOCS = NO -# The INTERNAL_DOCS tag determines if documentation -# that is typed after a \internal command is included. If the tag is set -# to NO (the default) then the documentation will be excluded. -# Set it to YES to include the internal documentation. +# The INTERNAL_DOCS tag determines if documentation that is typed after a +# \internal command is included. If the tag is set to NO then the documentation +# will be excluded. Set it to YES to include the internal documentation. +# The default value is: NO. INTERNAL_DOCS = NO -# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate -# file names in lower-case letters. If set to YES upper-case letters are also -# allowed. This is useful if you have classes or files whose names only differ -# in case and if your file system supports case sensitive file names. Windows -# and Mac users are advised to set this option to NO. +# With the correct setting of option CASE_SENSE_NAMES doxygen will better be +# able to match the capabilities of the underlying filesystem. In case the +# filesystem is case sensitive (i.e. it supports files in the same directory +# whose names only differ in casing), the option must be set to YES to properly +# deal with such files in case they appear in the input. For filesystems that +# are not case sensitive the option should be set to NO to properly deal with +# output files written for symbols that only differ in casing, such as for two +# classes, one named CLASS and the other named Class, and to also support +# references to files without having to specify the exact matching casing. On +# Windows (including Cygwin) and MacOS, users should typically set this option +# to NO, whereas on Linux or other Unix flavors it should typically be set to +# YES. +# Possible values are: SYSTEM, NO and YES. +# The default value is: SYSTEM. CASE_SENSE_NAMES = YES -# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen -# will show members with their full class and namespace scopes in the -# documentation. If set to YES the scope will be hidden. +# If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with +# their full class and namespace scopes in the documentation. If set to YES, the +# scope will be hidden. +# The default value is: NO. HIDE_SCOPE_NAMES = NO -# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen -# will put a list of the files that are included by a file in the documentation -# of that file. +# If the HIDE_COMPOUND_REFERENCE tag is set to NO (default) then doxygen will +# append additional text to a page's title, such as Class Reference. If set to +# YES the compound reference will be hidden. +# The default value is: NO. + +HIDE_COMPOUND_REFERENCE= NO + +# If the SHOW_HEADERFILE tag is set to YES then the documentation for a class +# will show which file needs to be included to use the class. +# The default value is: YES. + +SHOW_HEADERFILE = YES + +# If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of +# the files that are included by a file in the documentation of that file. +# The default value is: YES. SHOW_INCLUDE_FILES = YES -# If the FORCE_LOCAL_INCLUDES tag is set to YES then Doxygen -# will list include files with double quotes in the documentation -# rather than with sharp brackets. +# If the SHOW_GROUPED_MEMB_INC tag is set to YES then Doxygen will add for each +# grouped member an include statement to the documentation, telling the reader +# which file to include in order to use the member. +# The default value is: NO. + +SHOW_GROUPED_MEMB_INC = NO + +# If the FORCE_LOCAL_INCLUDES tag is set to YES then doxygen will list include +# files with double quotes in the documentation rather than with sharp brackets. +# The default value is: NO. FORCE_LOCAL_INCLUDES = NO -# If the INLINE_INFO tag is set to YES (the default) then a tag [inline] -# is inserted in the documentation for inline members. +# If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the +# documentation for inline members. +# The default value is: YES. INLINE_INFO = YES -# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen -# will sort the (detailed) documentation of file and class members -# alphabetically by member name. If set to NO the members will appear in -# declaration order. +# If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the +# (detailed) documentation of file and class members alphabetically by member +# name. If set to NO, the members will appear in declaration order. +# The default value is: YES. SORT_MEMBER_DOCS = YES -# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the -# brief documentation of file, namespace and class members alphabetically -# by member name. If set to NO (the default) the members will appear in -# declaration order. +# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief +# descriptions of file, namespace and class members alphabetically by member +# name. If set to NO, the members will appear in declaration order. Note that +# this will also influence the order of the classes in the class list. +# The default value is: NO. SORT_BRIEF_DOCS = NO -# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen -# will sort the (brief and detailed) documentation of class members so that -# constructors and destructors are listed first. If set to NO (the default) -# the constructors will appear in the respective orders defined by -# SORT_MEMBER_DOCS and SORT_BRIEF_DOCS. -# This tag will be ignored for brief docs if SORT_BRIEF_DOCS is set to NO -# and ignored for detailed docs if SORT_MEMBER_DOCS is set to NO. +# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the +# (brief and detailed) documentation of class members so that constructors and +# destructors are listed first. If set to NO the constructors will appear in the +# respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS. +# Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief +# member documentation. +# Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting +# detailed member documentation. +# The default value is: NO. SORT_MEMBERS_CTORS_1ST = NO -# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the -# hierarchy of group names into alphabetical order. If set to NO (the default) -# the group names will appear in their defined order. +# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the hierarchy +# of group names into alphabetical order. If set to NO the group names will +# appear in their defined order. +# The default value is: NO. SORT_GROUP_NAMES = NO -# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be -# sorted by fully-qualified names, including namespaces. If set to -# NO (the default), the class list will be sorted only by class name, -# not including the namespace part. +# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by +# fully-qualified names, including namespaces. If set to NO, the class list will +# be sorted only by class name, not including the namespace part. # Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. -# Note: This option applies only to the class list, not to the -# alphabetical list. +# Note: This option applies only to the class list, not to the alphabetical +# list. +# The default value is: NO. SORT_BY_SCOPE_NAME = NO -# The GENERATE_TODOLIST tag can be used to enable (YES) or -# disable (NO) the todo list. This list is created by putting \todo -# commands in the documentation. +# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper +# type resolution of all parameters of a function it will reject a match between +# the prototype and the implementation of a member function even if there is +# only one candidate or it is obvious which candidate to choose by doing a +# simple string match. By disabling STRICT_PROTO_MATCHING doxygen will still +# accept a match between prototype and implementation in such cases. +# The default value is: NO. + +STRICT_PROTO_MATCHING = NO + +# The GENERATE_TODOLIST tag can be used to enable (YES) or disable (NO) the todo +# list. This list is created by putting \todo commands in the documentation. +# The default value is: YES. GENERATE_TODOLIST = YES -# The GENERATE_TESTLIST tag can be used to enable (YES) or -# disable (NO) the test list. This list is created by putting \test -# commands in the documentation. +# The GENERATE_TESTLIST tag can be used to enable (YES) or disable (NO) the test +# list. This list is created by putting \test commands in the documentation. +# The default value is: YES. GENERATE_TESTLIST = YES -# The GENERATE_BUGLIST tag can be used to enable (YES) or -# disable (NO) the bug list. This list is created by putting \bug -# commands in the documentation. +# The GENERATE_BUGLIST tag can be used to enable (YES) or disable (NO) the bug +# list. This list is created by putting \bug commands in the documentation. +# The default value is: YES. GENERATE_BUGLIST = YES -# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or -# disable (NO) the deprecated list. This list is created by putting -# \deprecated commands in the documentation. +# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or disable (NO) +# the deprecated list. This list is created by putting \deprecated commands in +# the documentation. +# The default value is: YES. GENERATE_DEPRECATEDLIST= YES -# The ENABLED_SECTIONS tag can be used to enable conditional -# documentation sections, marked by \if sectionname ... \endif. +# The ENABLED_SECTIONS tag can be used to enable conditional documentation +# sections, marked by \if ... \endif and \cond +# ... \endcond blocks. ENABLED_SECTIONS = -# The MAX_INITIALIZER_LINES tag determines the maximum number of lines -# the initial value of a variable or define consists of for it to appear in -# the documentation. If the initializer consists of more lines than specified -# here it will be hidden. Use a value of 0 to hide initializers completely. -# The appearance of the initializer of individual variables and defines in the -# documentation can be controlled using \showinitializer or \hideinitializer -# command in the documentation regardless of this setting. +# The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the +# initial value of a variable or macro / define can have for it to appear in the +# documentation. If the initializer consists of more lines than specified here +# it will be hidden. Use a value of 0 to hide initializers completely. The +# appearance of the value of individual variables and macros / defines can be +# controlled using \showinitializer or \hideinitializer command in the +# documentation regardless of this setting. +# Minimum value: 0, maximum value: 10000, default value: 30. MAX_INITIALIZER_LINES = 30 -# Set the SHOW_USED_FILES tag to NO to disable the list of files generated -# at the bottom of the documentation of classes and structs. If set to YES the +# Set the SHOW_USED_FILES tag to NO to disable the list of files generated at +# the bottom of the documentation of classes and structs. If set to YES, the # list will mention the files that were used to generate the documentation. +# The default value is: YES. SHOW_USED_FILES = YES -# If the sources in your project are distributed over multiple directories -# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy -# in the documentation. The default is NO. - -SHOW_DIRECTORIES = NO - -# Set the SHOW_FILES tag to NO to disable the generation of the Files page. -# This will remove the Files entry from the Quick Index and from the -# Folder Tree View (if specified). The default is YES. +# Set the SHOW_FILES tag to NO to disable the generation of the Files page. This +# will remove the Files entry from the Quick Index and from the Folder Tree View +# (if specified). +# The default value is: YES. SHOW_FILES = YES -# Set the SHOW_NAMESPACES tag to NO to disable the generation of the -# Namespaces page. -# This will remove the Namespaces entry from the Quick Index -# and from the Folder Tree View (if specified). The default is YES. +# Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces +# page. This will remove the Namespaces entry from the Quick Index and from the +# Folder Tree View (if specified). +# The default value is: YES. SHOW_NAMESPACES = YES # The FILE_VERSION_FILTER tag can be used to specify a program or script that # doxygen should invoke to get the current version for each file (typically from # the version control system). Doxygen will invoke the program by executing (via -# popen()) the command , where is the value of -# the FILE_VERSION_FILTER tag, and is the name of an input file -# provided by doxygen. Whatever the program writes to standard output -# is used as the file version. See the manual for examples. +# popen()) the command command input-file, where command is the value of the +# FILE_VERSION_FILTER tag, and input-file is the name of an input file provided +# by doxygen. Whatever the program writes to standard output is used as the file +# version. For an example see the documentation. FILE_VERSION_FILTER = # The LAYOUT_FILE tag can be used to specify a layout file which will be parsed # by doxygen. The layout file controls the global structure of the generated -# output files in an output format independent way. The create the layout file -# that represents doxygen's defaults, run doxygen with the -l option. -# You can optionally specify a file name after the option, if omitted -# DoxygenLayout.xml will be used as the name of the layout file. +# output files in an output format independent way. To create the layout file +# that represents doxygen's defaults, run doxygen with the -l option. You can +# optionally specify a file name after the option, if omitted DoxygenLayout.xml +# will be used as the name of the layout file. See also section "Changing the +# layout of pages" for information. +# +# Note that if you run doxygen from a directory containing a file called +# DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE +# tag is left empty. LAYOUT_FILE = +# The CITE_BIB_FILES tag can be used to specify one or more bib files containing +# the reference definitions. This must be a list of .bib files. The .bib +# extension is automatically appended if omitted. This requires the bibtex tool +# to be installed. See also https://en.wikipedia.org/wiki/BibTeX for more info. +# For LaTeX the style of the bibliography can be controlled using +# LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the +# search path. See also \cite for info how to create references. + +CITE_BIB_FILES = + #--------------------------------------------------------------------------- -# configuration options related to warning and progress messages +# Configuration options related to warning and progress messages #--------------------------------------------------------------------------- -# The QUIET tag can be used to turn on/off the messages that are generated -# by doxygen. Possible values are YES and NO. If left blank NO is used. +# The QUIET tag can be used to turn on/off the messages that are generated to +# standard output by doxygen. If QUIET is set to YES this implies that the +# messages are off. +# The default value is: NO. QUIET = NO # The WARNINGS tag can be used to turn on/off the warning messages that are -# generated by doxygen. Possible values are YES and NO. If left blank -# NO is used. +# generated to standard error (stderr) by doxygen. If WARNINGS is set to YES +# this implies that the warnings are on. +# +# Tip: Turn warnings on while writing the documentation. +# The default value is: YES. WARNINGS = YES -# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings -# for undocumented members. If EXTRACT_ALL is set to YES then this flag will -# automatically be disabled. +# If the WARN_IF_UNDOCUMENTED tag is set to YES then doxygen will generate +# warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag +# will automatically be disabled. +# The default value is: YES. WARN_IF_UNDOCUMENTED = YES -# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for -# potential errors in the documentation, such as not documenting some -# parameters in a documented function, or documenting parameters that -# don't exist or using markup commands wrongly. +# If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for +# potential errors in the documentation, such as documenting some parameters in +# a documented function twice, or documenting parameters that don't exist or +# using markup commands wrongly. +# The default value is: YES. WARN_IF_DOC_ERROR = YES -# This WARN_NO_PARAMDOC option can be abled to get warnings for -# functions that are documented, but have no documentation for their parameters -# or return value. If set to NO (the default) doxygen will only warn about -# wrong or incomplete parameter documentation, but not about the absence of -# documentation. +# If WARN_IF_INCOMPLETE_DOC is set to YES, doxygen will warn about incomplete +# function parameter documentation. If set to NO, doxygen will accept that some +# parameters have no documentation without warning. +# The default value is: YES. + +WARN_IF_INCOMPLETE_DOC = YES + +# This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that +# are documented, but have no documentation for their parameters or return +# value. If set to NO, doxygen will only warn about wrong parameter +# documentation, but not about the absence of documentation. If EXTRACT_ALL is +# set to YES then this flag will automatically be disabled. See also +# WARN_IF_INCOMPLETE_DOC +# The default value is: NO. WARN_NO_PARAMDOC = NO -# The WARN_FORMAT tag determines the format of the warning messages that -# doxygen can produce. The string should contain the $file, $line, and $text -# tags, which will be replaced by the file and line number from which the -# warning originated and the warning text. Optionally the format may contain -# $version, which will be replaced by the version of the file (if it could -# be obtained via FILE_VERSION_FILTER) +# If WARN_IF_UNDOC_ENUM_VAL option is set to YES, doxygen will warn about +# undocumented enumeration values. If set to NO, doxygen will accept +# undocumented enumeration values. If EXTRACT_ALL is set to YES then this flag +# will automatically be disabled. +# The default value is: NO. + +WARN_IF_UNDOC_ENUM_VAL = NO + +# If the WARN_AS_ERROR tag is set to YES then doxygen will immediately stop when +# a warning is encountered. If the WARN_AS_ERROR tag is set to FAIL_ON_WARNINGS +# then doxygen will continue running as if WARN_AS_ERROR tag is set to NO, but +# at the end of the doxygen process doxygen will return with a non-zero status. +# Possible values are: NO, YES and FAIL_ON_WARNINGS. +# The default value is: NO. + +WARN_AS_ERROR = NO + +# The WARN_FORMAT tag determines the format of the warning messages that doxygen +# can produce. The string should contain the $file, $line, and $text tags, which +# will be replaced by the file and line number from which the warning originated +# and the warning text. Optionally the format may contain $version, which will +# be replaced by the version of the file (if it could be obtained via +# FILE_VERSION_FILTER) +# See also: WARN_LINE_FORMAT +# The default value is: $file:$line: $text. WARN_FORMAT = "$file:$line: $text" -# The WARN_LOGFILE tag can be used to specify a file to which warning -# and error messages should be written. If left blank the output is written -# to stderr. +# In the $text part of the WARN_FORMAT command it is possible that a reference +# to a more specific place is given. To make it easier to jump to this place +# (outside of doxygen) the user can define a custom "cut" / "paste" string. +# Example: +# WARN_LINE_FORMAT = "'vi $file +$line'" +# See also: WARN_FORMAT +# The default value is: at line $line of file $file. + +WARN_LINE_FORMAT = "at line $line of file $file" + +# The WARN_LOGFILE tag can be used to specify a file to which warning and error +# messages should be written. If left blank the output is written to standard +# error (stderr). In case the file specified cannot be opened for writing the +# warning and error messages are written to standard error. When as file - is +# specified the warning and error messages are written to standard output +# (stdout). WARN_LOGFILE = #--------------------------------------------------------------------------- -# configuration options related to the input files +# Configuration options related to the input files #--------------------------------------------------------------------------- -# The INPUT tag can be used to specify the files and/or directories that contain -# documented source files. You may enter file names like "myfile.cpp" or -# directories like "/usr/src/myproject". Separate the files or directories -# with spaces. +# The INPUT tag is used to specify the files and/or directories that contain +# documented source files. You may enter file names like myfile.cpp or +# directories like /usr/src/myproject. Separate the files or directories with +# spaces. See also FILE_PATTERNS and EXTENSION_MAPPING +# Note: If this tag is empty the current directory is searched. -INPUT = ../hidapi +INPUT = ../hidapi \ + ./main_page.md # This tag can be used to specify the character encoding of the source files -# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is -# also the default input encoding. Doxygen uses libiconv (or the iconv built -# into libc) for the transcoding. See http://www.gnu.org/software/libiconv for -# the list of possible encodings. +# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses +# libiconv (or the iconv built into libc) for the transcoding. See the libiconv +# documentation (see: +# https://www.gnu.org/software/libiconv/) for the list of possible encodings. +# See also: INPUT_FILE_ENCODING +# The default value is: UTF-8. INPUT_ENCODING = UTF-8 +# This tag can be used to specify the character encoding of the source files +# that doxygen parses The INPUT_FILE_ENCODING tag can be used to specify +# character encoding on a per file pattern basis. Doxygen will compare the file +# name with each pattern and apply the encoding instead of the default +# INPUT_ENCODING) if there is a match. The character encodings are a list of the +# form: pattern=encoding (like *.php=ISO-8859-1). See cfg_input_encoding +# "INPUT_ENCODING" for further information on supported encodings. + +INPUT_FILE_ENCODING = + # If the value of the INPUT tag contains directories, you can use the -# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp -# and *.h) to filter out the source-files in the directories. If left -# blank the following patterns are tested: -# *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx -# *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.py *.f90 +# FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and +# *.h) to filter out the source-files in the directories. +# +# Note that for custom extensions or not directly supported extensions you also +# need to set EXTENSION_MAPPING for the extension otherwise the files are not +# read by doxygen. +# +# Note the list of default checked file patterns might differ from the list of +# default file extension mappings. +# +# If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cpp, +# *.c++, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h, +# *.hh, *.hxx, *.hpp, *.h++, *.l, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, +# *.inc, *.m, *.markdown, *.md, *.mm, *.dox (to be provided as doxygen C +# comment), *.py, *.pyw, *.f90, *.f95, *.f03, *.f08, *.f18, *.f, *.for, *.vhd, +# *.vhdl, *.ucf, *.qsf and *.ice. FILE_PATTERNS = -# The RECURSIVE tag can be used to turn specify whether or not subdirectories -# should be searched for input files as well. Possible values are YES and NO. -# If left blank NO is used. +# The RECURSIVE tag can be used to specify whether or not subdirectories should +# be searched for input files as well. +# The default value is: NO. RECURSIVE = NO -# The EXCLUDE tag can be used to specify files and/or directories that should +# The EXCLUDE tag can be used to specify files and/or directories that should be # excluded from the INPUT source files. This way you can easily exclude a # subdirectory from a directory tree whose root is specified with the INPUT tag. +# +# Note that relative paths are relative to the directory from which doxygen is +# run. EXCLUDE = -# The EXCLUDE_SYMLINKS tag can be used select whether or not files or -# directories that are symbolic links (a Unix filesystem feature) are excluded +# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or +# directories that are symbolic links (a Unix file system feature) are excluded # from the input. +# The default value is: NO. EXCLUDE_SYMLINKS = NO # If the value of the INPUT tag contains directories, you can use the # EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude -# certain files from those directories. Note that the wildcards are matched -# against the file with absolute path, so to exclude all test directories -# for example use the pattern */test/* +# certain files from those directories. +# +# Note that the wildcards are matched against the file with absolute path, so to +# exclude all test directories for example use the pattern */test/* EXCLUDE_PATTERNS = @@ -630,689 +985,1278 @@ EXCLUDE_PATTERNS = # (namespaces, classes, functions, etc.) that should be excluded from the # output. The symbol name can be a fully qualified name, a word, or if the # wildcard * is used, a substring. Examples: ANamespace, AClass, -# AClass::ANamespace, ANamespace::*Test +# ANamespace::AClass, ANamespace::*Test +# +# Note that the wildcards are matched against the file with absolute path, so to +# exclude all test directories use the pattern */test/* -EXCLUDE_SYMBOLS = +EXCLUDE_SYMBOLS = HID_API_AS_STR_IMPL \ + HID_API_AS_STR \ + HID_API_TO_VERSION_STR -# The EXAMPLE_PATH tag can be used to specify one or more files or -# directories that contain example code fragments that are included (see -# the \include command). +# The EXAMPLE_PATH tag can be used to specify one or more files or directories +# that contain example code fragments that are included (see the \include +# command). -EXAMPLE_PATH = +EXAMPLE_PATH = ../hidtest # If the value of the EXAMPLE_PATH tag contains directories, you can use the -# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp -# and *.h) to filter out the source-files in the directories. If left -# blank all files are included. +# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and +# *.h) to filter out the source-files in the directories. If left blank all +# files are included. -EXAMPLE_PATTERNS = +EXAMPLE_PATTERNS = *.c # If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be -# searched for input files to be used with the \include or \dontinclude -# commands irrespective of the value of the RECURSIVE tag. -# Possible values are YES and NO. If left blank NO is used. +# searched for input files to be used with the \include or \dontinclude commands +# irrespective of the value of the RECURSIVE tag. +# The default value is: NO. EXAMPLE_RECURSIVE = NO -# The IMAGE_PATH tag can be used to specify one or more files or -# directories that contain image that are included in the documentation (see -# the \image command). +# The IMAGE_PATH tag can be used to specify one or more files or directories +# that contain images that are to be included in the documentation (see the +# \image command). IMAGE_PATH = # The INPUT_FILTER tag can be used to specify a program that doxygen should # invoke to filter for each input file. Doxygen will invoke the filter program -# by executing (via popen()) the command , where -# is the value of the INPUT_FILTER tag, and is the name of an -# input file. Doxygen will then use the output that the filter program writes -# to standard output. -# If FILTER_PATTERNS is specified, this tag will be -# ignored. +# by executing (via popen()) the command: +# +# +# +# where is the value of the INPUT_FILTER tag, and is the +# name of an input file. Doxygen will then use the output that the filter +# program writes to standard output. If FILTER_PATTERNS is specified, this tag +# will be ignored. +# +# Note that the filter must not add or remove lines; it is applied before the +# code is scanned, but not when the output code is generated. If lines are added +# or removed, the anchors will not be placed correctly. +# +# Note that doxygen will use the data processed and written to standard output +# for further processing, therefore nothing else, like debug statements or used +# commands (so in case of a Windows batch file always use @echo OFF), should be +# written to standard output. +# +# Note that for custom extensions or not directly supported extensions you also +# need to set EXTENSION_MAPPING for the extension otherwise the files are not +# properly processed by doxygen. INPUT_FILTER = # The FILTER_PATTERNS tag can be used to specify filters on a per file pattern -# basis. -# Doxygen will compare the file name with each pattern and apply the -# filter if there is a match. -# The filters are a list of the form: -# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further -# info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER -# is applied to all files. +# basis. Doxygen will compare the file name with each pattern and apply the +# filter if there is a match. The filters are a list of the form: pattern=filter +# (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how +# filters are used. If the FILTER_PATTERNS tag is empty or if none of the +# patterns match the file name, INPUT_FILTER is applied. +# +# Note that for custom extensions or not directly supported extensions you also +# need to set EXTENSION_MAPPING for the extension otherwise the files are not +# properly processed by doxygen. FILTER_PATTERNS = # If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using -# INPUT_FILTER) will be used to filter the input files when producing source -# files to browse (i.e. when SOURCE_BROWSER is set to YES). +# INPUT_FILTER) will also be used to filter the input files that are used for +# producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES). +# The default value is: NO. FILTER_SOURCE_FILES = NO +# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file +# pattern. A pattern will override the setting for FILTER_PATTERN (if any) and +# it is also possible to disable source filtering for a specific pattern using +# *.ext= (so without naming a filter). +# This tag requires that the tag FILTER_SOURCE_FILES is set to YES. + +FILTER_SOURCE_PATTERNS = + +# If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that +# is part of the input, its contents will be placed on the main page +# (index.html). This can be useful if you have a project on for instance GitHub +# and want to reuse the introduction page also for the doxygen output. + +USE_MDFILE_AS_MAINPAGE = main_page.md + +# The Fortran standard specifies that for fixed formatted Fortran code all +# characters from position 72 are to be considered as comment. A common +# extension is to allow longer lines before the automatic comment starts. The +# setting FORTRAN_COMMENT_AFTER will also make it possible that longer lines can +# be processed before the automatic comment starts. +# Minimum value: 7, maximum value: 10000, default value: 72. + +FORTRAN_COMMENT_AFTER = 72 + #--------------------------------------------------------------------------- -# configuration options related to source browsing +# Configuration options related to source browsing #--------------------------------------------------------------------------- -# If the SOURCE_BROWSER tag is set to YES then a list of source files will -# be generated. Documented entities will be cross-referenced with these sources. -# Note: To get rid of all source code in the generated output, make sure also -# VERBATIM_HEADERS is set to NO. +# If the SOURCE_BROWSER tag is set to YES then a list of source files will be +# generated. Documented entities will be cross-referenced with these sources. +# +# Note: To get rid of all source code in the generated output, make sure that +# also VERBATIM_HEADERS is set to NO. +# The default value is: NO. SOURCE_BROWSER = NO -# Setting the INLINE_SOURCES tag to YES will include the body -# of functions and classes directly in the documentation. +# Setting the INLINE_SOURCES tag to YES will include the body of functions, +# classes and enums directly into the documentation. +# The default value is: NO. INLINE_SOURCES = NO -# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct -# doxygen to hide any special comment blocks from generated source code -# fragments. Normal C and C++ comments will always remain visible. +# Setting the STRIP_CODE_COMMENTS tag to YES will instruct doxygen to hide any +# special comment blocks from generated source code fragments. Normal C, C++ and +# Fortran comments will always remain visible. +# The default value is: YES. STRIP_CODE_COMMENTS = YES -# If the REFERENCED_BY_RELATION tag is set to YES -# then for each documented function all documented -# functions referencing it will be listed. +# If the REFERENCED_BY_RELATION tag is set to YES then for each documented +# entity all documented functions referencing it will be listed. +# The default value is: NO. REFERENCED_BY_RELATION = NO -# If the REFERENCES_RELATION tag is set to YES -# then for each documented function all documented entities -# called/used by that function will be listed. +# If the REFERENCES_RELATION tag is set to YES then for each documented function +# all documented entities called/used by that function will be listed. +# The default value is: NO. REFERENCES_RELATION = NO -# If the REFERENCES_LINK_SOURCE tag is set to YES (the default) -# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from -# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will -# link to the source code. -# Otherwise they will link to the documentation. +# If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set +# to YES then the hyperlinks from functions in REFERENCES_RELATION and +# REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will +# link to the documentation. +# The default value is: YES. REFERENCES_LINK_SOURCE = YES -# If the USE_HTAGS tag is set to YES then the references to source code -# will point to the HTML generated by the htags(1) tool instead of doxygen -# built-in source browser. The htags tool is part of GNU's global source -# tagging system (see http://www.gnu.org/software/global/global.html). You -# will need version 4.8.6 or higher. +# If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the +# source code will show a tooltip with additional information such as prototype, +# brief description and links to the definition and documentation. Since this +# will make the HTML file larger and loading of large files a bit slower, you +# can opt to disable this feature. +# The default value is: YES. +# This tag requires that the tag SOURCE_BROWSER is set to YES. + +SOURCE_TOOLTIPS = YES + +# If the USE_HTAGS tag is set to YES then the references to source code will +# point to the HTML generated by the htags(1) tool instead of doxygen built-in +# source browser. The htags tool is part of GNU's global source tagging system +# (see https://www.gnu.org/software/global/global.html). You will need version +# 4.8.6 or higher. +# +# To use it do the following: +# - Install the latest version of global +# - Enable SOURCE_BROWSER and USE_HTAGS in the configuration file +# - Make sure the INPUT points to the root of the source tree +# - Run doxygen as normal +# +# Doxygen will invoke htags (and that will in turn invoke gtags), so these +# tools must be available from the command line (i.e. in the search path). +# +# The result: instead of the source browser generated by doxygen, the links to +# source code will now point to the output of htags. +# The default value is: NO. +# This tag requires that the tag SOURCE_BROWSER is set to YES. USE_HTAGS = NO -# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen -# will generate a verbatim copy of the header file for each class for -# which an include is specified. Set to NO to disable this. +# If the VERBATIM_HEADERS tag is set the YES then doxygen will generate a +# verbatim copy of the header file for each class for which an include is +# specified. Set to NO to disable this. +# See also: Section \class. +# The default value is: YES. VERBATIM_HEADERS = YES +# If the CLANG_ASSISTED_PARSING tag is set to YES then doxygen will use the +# clang parser (see: +# http://clang.llvm.org/) for more accurate parsing at the cost of reduced +# performance. This can be particularly helpful with template rich C++ code for +# which doxygen's built-in parser lacks the necessary type information. +# Note: The availability of this option depends on whether or not doxygen was +# generated with the -Duse_libclang=ON option for CMake. +# The default value is: NO. + +CLANG_ASSISTED_PARSING = NO + +# If the CLANG_ASSISTED_PARSING tag is set to YES and the CLANG_ADD_INC_PATHS +# tag is set to YES then doxygen will add the directory of each input to the +# include path. +# The default value is: YES. +# This tag requires that the tag CLANG_ASSISTED_PARSING is set to YES. + +CLANG_ADD_INC_PATHS = YES + +# If clang assisted parsing is enabled you can provide the compiler with command +# line options that you would normally use when invoking the compiler. Note that +# the include paths will already be set by doxygen for the files and directories +# specified with INPUT and INCLUDE_PATH. +# This tag requires that the tag CLANG_ASSISTED_PARSING is set to YES. + +CLANG_OPTIONS = + +# If clang assisted parsing is enabled you can provide the clang parser with the +# path to the directory containing a file called compile_commands.json. This +# file is the compilation database (see: +# http://clang.llvm.org/docs/HowToSetupToolingForLLVM.html) containing the +# options used when the source files were built. This is equivalent to +# specifying the -p option to a clang tool, such as clang-check. These options +# will then be passed to the parser. Any options specified with CLANG_OPTIONS +# will be added as well. +# Note: The availability of this option depends on whether or not doxygen was +# generated with the -Duse_libclang=ON option for CMake. + +CLANG_DATABASE_PATH = + #--------------------------------------------------------------------------- -# configuration options related to the alphabetical class index +# Configuration options related to the alphabetical class index #--------------------------------------------------------------------------- -# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index -# of all compounds will be generated. Enable this if the project -# contains a lot of classes, structs, unions or interfaces. +# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index of all +# compounds will be generated. Enable this if the project contains a lot of +# classes, structs, unions or interfaces. +# The default value is: YES. ALPHABETICAL_INDEX = YES -# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then -# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns -# in which this list will be split (can be a number in the range [1..20]) - -COLS_IN_ALPHA_INDEX = 5 - -# In case all classes in a project start with a common prefix, all -# classes will be put under the same header in the alphabetical index. -# The IGNORE_PREFIX tag can be used to specify one or more prefixes that -# should be ignored while generating the index headers. +# The IGNORE_PREFIX tag can be used to specify a prefix (or a list of prefixes) +# that should be ignored while generating the index headers. The IGNORE_PREFIX +# tag works for classes, function and member names. The entity will be placed in +# the alphabetical list under the first letter of the entity name that remains +# after removing the prefix. +# This tag requires that the tag ALPHABETICAL_INDEX is set to YES. IGNORE_PREFIX = #--------------------------------------------------------------------------- -# configuration options related to the HTML output +# Configuration options related to the HTML output #--------------------------------------------------------------------------- -# If the GENERATE_HTML tag is set to YES (the default) Doxygen will -# generate HTML output. +# If the GENERATE_HTML tag is set to YES, doxygen will generate HTML output +# The default value is: YES. GENERATE_HTML = YES -# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. -# If a relative path is entered the value of OUTPUT_DIRECTORY will be -# put in front of it. If left blank `html' will be used as the default path. +# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a +# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of +# it. +# The default directory is: html. +# This tag requires that the tag GENERATE_HTML is set to YES. HTML_OUTPUT = html -# The HTML_FILE_EXTENSION tag can be used to specify the file extension for -# each generated HTML page (for example: .htm,.php,.asp). If it is left blank -# doxygen will generate files with .html extension. +# The HTML_FILE_EXTENSION tag can be used to specify the file extension for each +# generated HTML page (for example: .htm, .php, .asp). +# The default value is: .html. +# This tag requires that the tag GENERATE_HTML is set to YES. HTML_FILE_EXTENSION = .html -# The HTML_HEADER tag can be used to specify a personal HTML header for -# each generated HTML page. If it is left blank doxygen will generate a +# The HTML_HEADER tag can be used to specify a user-defined HTML header file for +# each generated HTML page. If the tag is left blank doxygen will generate a # standard header. +# +# To get valid HTML the header file that includes any scripts and style sheets +# that doxygen needs, which is dependent on the configuration options used (e.g. +# the setting GENERATE_TREEVIEW). It is highly recommended to start with a +# default header using +# doxygen -w html new_header.html new_footer.html new_stylesheet.css +# YourConfigFile +# and then modify the file new_header.html. See also section "Doxygen usage" +# for information on how to generate the default header that doxygen normally +# uses. +# Note: The header is subject to change so you typically have to regenerate the +# default header when upgrading to a newer version of doxygen. For a description +# of the possible markers and block names see the documentation. +# This tag requires that the tag GENERATE_HTML is set to YES. HTML_HEADER = -# The HTML_FOOTER tag can be used to specify a personal HTML footer for -# each generated HTML page. If it is left blank doxygen will generate a -# standard footer. +# The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each +# generated HTML page. If the tag is left blank doxygen will generate a standard +# footer. See HTML_HEADER for more information on how to generate a default +# footer and what special commands can be used inside the footer. See also +# section "Doxygen usage" for information on how to generate the default footer +# that doxygen normally uses. +# This tag requires that the tag GENERATE_HTML is set to YES. HTML_FOOTER = -# The HTML_STYLESHEET tag can be used to specify a user-defined cascading -# style sheet that is used by each HTML page. It can be used to -# fine-tune the look of the HTML output. If the tag is left blank doxygen -# will generate a default style sheet. Note that doxygen will try to copy -# the style sheet file to the HTML output directory, so don't put your own -# stylesheet in the HTML output directory as well, or it will be erased! +# The HTML_STYLESHEET tag can be used to specify a user-defined cascading style +# sheet that is used by each HTML page. It can be used to fine-tune the look of +# the HTML output. If left blank doxygen will generate a default style sheet. +# See also section "Doxygen usage" for information on how to generate the style +# sheet that doxygen normally uses. +# Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as +# it is more robust and this tag (HTML_STYLESHEET) will in the future become +# obsolete. +# This tag requires that the tag GENERATE_HTML is set to YES. HTML_STYLESHEET = -# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. -# Doxygen will adjust the colors in the stylesheet and background images -# according to this color. Hue is specified as an angle on a colorwheel, -# see http://en.wikipedia.org/wiki/Hue for more information. -# For instance the value 0 represents red, 60 is yellow, 120 is green, -# 180 is cyan, 240 is blue, 300 purple, and 360 is red again. -# The allowed range is 0 to 359. +# The HTML_EXTRA_STYLESHEET tag can be used to specify additional user-defined +# cascading style sheets that are included after the standard style sheets +# created by doxygen. Using this option one can overrule certain style aspects. +# This is preferred over using HTML_STYLESHEET since it does not replace the +# standard style sheet and is therefore more robust against future updates. +# Doxygen will copy the style sheet files to the output directory. +# Note: The order of the extra style sheet files is of importance (e.g. the last +# style sheet in the list overrules the setting of the previous ones in the +# list). +# Note: Since the styling of scrollbars can currently not be overruled in +# Webkit/Chromium, the styling will be left out of the default doxygen.css if +# one or more extra stylesheets have been specified. So if scrollbar +# customization is desired it has to be added explicitly. For an example see the +# documentation. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_EXTRA_STYLESHEET = + +# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or +# other source files which should be copied to the HTML output directory. Note +# that these files will be copied to the base HTML output directory. Use the +# $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these +# files. In the HTML_STYLESHEET file, use the file name only. Also note that the +# files will be copied as-is; there are no commands or markers available. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_EXTRA_FILES = + +# The HTML_COLORSTYLE tag can be used to specify if the generated HTML output +# should be rendered with a dark or light theme. +# Possible values are: LIGHT always generate light mode output, DARK always +# generate dark mode output, AUTO_LIGHT automatically set the mode according to +# the user preference, use light mode if no preference is set (the default), +# AUTO_DARK automatically set the mode according to the user preference, use +# dark mode if no preference is set and TOGGLE allow to user to switch between +# light and dark mode via a button. +# The default value is: AUTO_LIGHT. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE = AUTO_LIGHT + +# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen +# will adjust the colors in the style sheet and background images according to +# this color. Hue is specified as an angle on a color-wheel, see +# https://en.wikipedia.org/wiki/Hue for more information. For instance the value +# 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300 +# purple, and 360 is red again. +# Minimum value: 0, maximum value: 359, default value: 220. +# This tag requires that the tag GENERATE_HTML is set to YES. HTML_COLORSTYLE_HUE = 220 -# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of -# the colors in the HTML output. For a value of 0 the output will use -# grayscales only. A value of 255 will produce the most vivid colors. +# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors +# in the HTML output. For a value of 0 the output will use gray-scales only. A +# value of 255 will produce the most vivid colors. +# Minimum value: 0, maximum value: 255, default value: 100. +# This tag requires that the tag GENERATE_HTML is set to YES. HTML_COLORSTYLE_SAT = 100 -# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to -# the luminance component of the colors in the HTML output. Values below -# 100 gradually make the output lighter, whereas values above 100 make -# the output darker. The value divided by 100 is the actual gamma applied, -# so 80 represents a gamma of 0.8, The value 220 represents a gamma of 2.2, -# and 100 does not change the gamma. +# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the +# luminance component of the colors in the HTML output. Values below 100 +# gradually make the output lighter, whereas values above 100 make the output +# darker. The value divided by 100 is the actual gamma applied, so 80 represents +# a gamma of 0.8, The value 220 represents a gamma of 2.2, and 100 does not +# change the gamma. +# Minimum value: 40, maximum value: 240, default value: 80. +# This tag requires that the tag GENERATE_HTML is set to YES. HTML_COLORSTYLE_GAMMA = 80 # If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML -# page will contain the date and time when the page was generated. Setting -# this to NO can help when comparing the output of multiple runs. +# page will contain the date and time when the page was generated. Setting this +# to YES can help to show when doxygen was last run and thus if the +# documentation is up to date. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. HTML_TIMESTAMP = YES -# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, -# files or namespaces will be aligned in HTML using tables. If set to -# NO a bullet list will be used. +# If the HTML_DYNAMIC_MENUS tag is set to YES then the generated HTML +# documentation will contain a main index with vertical navigation menus that +# are dynamically created via JavaScript. If disabled, the navigation index will +# consists of multiple levels of tabs that are statically embedded in every HTML +# page. Disable this option to support browsers that do not have JavaScript, +# like the Qt help browser. +# The default value is: YES. +# This tag requires that the tag GENERATE_HTML is set to YES. -HTML_ALIGN_MEMBERS = YES +HTML_DYNAMIC_MENUS = YES # If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML # documentation will contain sections that can be hidden and shown after the -# page has loaded. For this to work a browser that supports -# JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox -# Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari). +# page has loaded. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. HTML_DYNAMIC_SECTIONS = NO -# If the GENERATE_DOCSET tag is set to YES, additional index files -# will be generated that can be used as input for Apple's Xcode 3 -# integrated development environment, introduced with OSX 10.5 (Leopard). -# To create a documentation set, doxygen will generate a Makefile in the -# HTML output directory. Running make will produce the docset in that -# directory and running "make install" will install the docset in -# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find -# it at startup. -# See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html -# for more information. +# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries +# shown in the various tree structured indices initially; the user can expand +# and collapse entries dynamically later on. Doxygen will expand the tree to +# such a level that at most the specified number of entries are visible (unless +# a fully collapsed tree already exceeds this amount). So setting the number of +# entries 1 will produce a full collapsed tree by default. 0 is a special value +# representing an infinite number of entries and will result in a full expanded +# tree by default. +# Minimum value: 0, maximum value: 9999, default value: 100. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_INDEX_NUM_ENTRIES = 100 + +# If the GENERATE_DOCSET tag is set to YES, additional index files will be +# generated that can be used as input for Apple's Xcode 3 integrated development +# environment (see: +# https://developer.apple.com/xcode/), introduced with OSX 10.5 (Leopard). To +# create a documentation set, doxygen will generate a Makefile in the HTML +# output directory. Running make will produce the docset in that directory and +# running make install will install the docset in +# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at +# startup. See https://developer.apple.com/library/archive/featuredarticles/Doxy +# genXcode/_index.html for more information. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_DOCSET = NO -# When GENERATE_DOCSET tag is set to YES, this tag determines the name of the -# feed. A documentation feed provides an umbrella under which multiple -# documentation sets from a single provider (such as a company or product suite) -# can be grouped. +# This tag determines the name of the docset feed. A documentation feed provides +# an umbrella under which multiple documentation sets from a single provider +# (such as a company or product suite) can be grouped. +# The default value is: Doxygen generated docs. +# This tag requires that the tag GENERATE_DOCSET is set to YES. DOCSET_FEEDNAME = "Doxygen generated docs" -# When GENERATE_DOCSET tag is set to YES, this tag specifies a string that -# should uniquely identify the documentation set bundle. This should be a -# reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen -# will append .docset to the name. +# This tag determines the URL of the docset feed. A documentation feed provides +# an umbrella under which multiple documentation sets from a single provider +# (such as a company or product suite) can be grouped. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_FEEDURL = + +# This tag specifies a string that should uniquely identify the documentation +# set bundle. This should be a reverse domain-name style string, e.g. +# com.mycompany.MyDocSet. Doxygen will append .docset to the name. +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_DOCSET is set to YES. DOCSET_BUNDLE_ID = org.doxygen.Project -# When GENERATE_PUBLISHER_ID tag specifies a string that should uniquely identify +# The DOCSET_PUBLISHER_ID tag specifies a string that should uniquely identify # the documentation publisher. This should be a reverse domain-name style # string, e.g. com.mycompany.MyDocSet.documentation. +# The default value is: org.doxygen.Publisher. +# This tag requires that the tag GENERATE_DOCSET is set to YES. DOCSET_PUBLISHER_ID = org.doxygen.Publisher -# The GENERATE_PUBLISHER_NAME tag identifies the documentation publisher. +# The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher. +# The default value is: Publisher. +# This tag requires that the tag GENERATE_DOCSET is set to YES. DOCSET_PUBLISHER_NAME = Publisher -# If the GENERATE_HTMLHELP tag is set to YES, additional index files -# will be generated that can be used as input for tools like the -# Microsoft HTML help workshop to generate a compiled HTML help file (.chm) -# of the generated HTML documentation. +# If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three +# additional HTML index files: index.hhp, index.hhc, and index.hhk. The +# index.hhp is a project file that can be read by Microsoft's HTML Help Workshop +# on Windows. In the beginning of 2021 Microsoft took the original page, with +# a.o. the download links, offline the HTML help workshop was already many years +# in maintenance mode). You can download the HTML help workshop from the web +# archives at Installation executable (see: +# http://web.archive.org/web/20160201063255/http://download.microsoft.com/downlo +# ad/0/A/9/0A939EF6-E31C-430F-A3DF-DFAE7960D564/htmlhelp.exe). +# +# The HTML Help Workshop contains a compiler that can convert all HTML output +# generated by doxygen into a single compiled HTML file (.chm). Compiled HTML +# files are now used as the Windows 98 help format, and will replace the old +# Windows help format (.hlp) on all Windows platforms in the future. Compressed +# HTML files also contain an index, a table of contents, and you can search for +# words in the documentation. The HTML workshop also contains a viewer for +# compressed HTML files. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_HTMLHELP = NO -# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can -# be used to specify the file name of the resulting .chm file. You -# can add a path in front of the file if the result should not be +# The CHM_FILE tag can be used to specify the file name of the resulting .chm +# file. You can add a path in front of the file if the result should not be # written to the html output directory. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. CHM_FILE = -# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can -# be used to specify the location (absolute path including file name) of -# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run -# the HTML help compiler on the generated index.hhp. +# The HHC_LOCATION tag can be used to specify the location (absolute path +# including file name) of the HTML help compiler (hhc.exe). If non-empty, +# doxygen will try to run the HTML help compiler on the generated index.hhp. +# The file has to be specified with full path. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. HHC_LOCATION = -# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag -# controls if a separate .chi index file is generated (YES) or that -# it should be included in the master .chm file (NO). +# The GENERATE_CHI flag controls if a separate .chi index file is generated +# (YES) or that it should be included in the main .chm file (NO). +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. GENERATE_CHI = NO -# If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING -# is used to encode HtmlHelp index (hhk), content (hhc) and project file -# content. +# The CHM_INDEX_ENCODING is used to encode HtmlHelp index (hhk), content (hhc) +# and project file content. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. CHM_INDEX_ENCODING = -# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag -# controls whether a binary table of contents is generated (YES) or a -# normal table of contents (NO) in the .chm file. +# The BINARY_TOC flag controls whether a binary table of contents is generated +# (YES) or a normal table of contents (NO) in the .chm file. Furthermore it +# enables the Previous and Next buttons. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. BINARY_TOC = NO -# The TOC_EXPAND flag can be set to YES to add extra items for group members -# to the contents of the HTML help documentation and to the tree view. +# The TOC_EXPAND flag can be set to YES to add extra items for group members to +# the table of contents of the HTML help documentation and to the tree view. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. TOC_EXPAND = NO # If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and -# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated -# that can be used as input for Qt's qhelpgenerator to generate a -# Qt Compressed Help (.qch) of the generated HTML documentation. +# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that +# can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help +# (.qch) of the generated HTML documentation. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_QHP = NO -# If the QHG_LOCATION tag is specified, the QCH_FILE tag can -# be used to specify the file name of the resulting .qch file. -# The path specified is relative to the HTML output folder. +# If the QHG_LOCATION tag is specified, the QCH_FILE tag can be used to specify +# the file name of the resulting .qch file. The path specified is relative to +# the HTML output folder. +# This tag requires that the tag GENERATE_QHP is set to YES. QCH_FILE = -# The QHP_NAMESPACE tag specifies the namespace to use when generating -# Qt Help Project output. For more information please see -# http://doc.trolltech.com/qthelpproject.html#namespace +# The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help +# Project output. For more information please see Qt Help Project / Namespace +# (see: +# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#namespace). +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_QHP is set to YES. QHP_NAMESPACE = org.doxygen.Project -# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating -# Qt Help Project output. For more information please see -# http://doc.trolltech.com/qthelpproject.html#virtual-folders +# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt +# Help Project output. For more information please see Qt Help Project / Virtual +# Folders (see: +# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#virtual-folders). +# The default value is: doc. +# This tag requires that the tag GENERATE_QHP is set to YES. QHP_VIRTUAL_FOLDER = doc -# If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to -# add. For more information please see -# http://doc.trolltech.com/qthelpproject.html#custom-filters +# If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom +# filter to add. For more information please see Qt Help Project / Custom +# Filters (see: +# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-filters). +# This tag requires that the tag GENERATE_QHP is set to YES. QHP_CUST_FILTER_NAME = -# The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the -# custom filter to add. For more information please see -# -# Qt Help Project / Custom Filters. +# The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the +# custom filter to add. For more information please see Qt Help Project / Custom +# Filters (see: +# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-filters). +# This tag requires that the tag GENERATE_QHP is set to YES. QHP_CUST_FILTER_ATTRS = # The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this -# project's -# filter section matches. -# -# Qt Help Project / Filter Attributes. +# project's filter section matches. Qt Help Project / Filter Attributes (see: +# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#filter-attributes). +# This tag requires that the tag GENERATE_QHP is set to YES. QHP_SECT_FILTER_ATTRS = -# If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can -# be used to specify the location of Qt's qhelpgenerator. -# If non-empty doxygen will try to run qhelpgenerator on the generated -# .qhp file. +# The QHG_LOCATION tag can be used to specify the location (absolute path +# including file name) of Qt's qhelpgenerator. If non-empty doxygen will try to +# run qhelpgenerator on the generated .qhp file. +# This tag requires that the tag GENERATE_QHP is set to YES. QHG_LOCATION = -# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files -# will be generated, which together with the HTML files, form an Eclipse help -# plugin. To install this plugin and make it available under the help contents -# menu in Eclipse, the contents of the directory containing the HTML and XML -# files needs to be copied into the plugins directory of eclipse. The name of -# the directory within the plugins directory should be the same as -# the ECLIPSE_DOC_ID value. After copying Eclipse needs to be restarted before -# the help appears. +# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be +# generated, together with the HTML files, they form an Eclipse help plugin. To +# install this plugin and make it available under the help contents menu in +# Eclipse, the contents of the directory containing the HTML and XML files needs +# to be copied into the plugins directory of eclipse. The name of the directory +# within the plugins directory should be the same as the ECLIPSE_DOC_ID value. +# After copying Eclipse needs to be restarted before the help appears. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_ECLIPSEHELP = NO -# A unique identifier for the eclipse help plugin. When installing the plugin -# the directory name containing the HTML and XML files should also have -# this name. +# A unique identifier for the Eclipse help plugin. When installing the plugin +# the directory name containing the HTML and XML files should also have this +# name. Each documentation set should have its own identifier. +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES. ECLIPSE_DOC_ID = org.doxygen.Project -# The DISABLE_INDEX tag can be used to turn on/off the condensed index at -# top of each HTML page. The value NO (the default) enables the index and -# the value YES disables it. +# If you want full control over the layout of the generated HTML pages it might +# be necessary to disable the index and replace it with your own. The +# DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top +# of each HTML page. A value of NO enables the index and the value YES disables +# it. Since the tabs in the index contain the same information as the navigation +# tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. DISABLE_INDEX = NO -# This tag can be used to set the number of enum values (range [1..20]) -# that doxygen will group on one line in the generated HTML documentation. - -ENUM_VALUES_PER_LINE = 4 - # The GENERATE_TREEVIEW tag is used to specify whether a tree-like index -# structure should be generated to display hierarchical information. -# If the tag value is set to YES, a side panel will be generated -# containing a tree-like index structure (just like the one that -# is generated for HTML Help). For this to work a browser that supports -# JavaScript, DHTML, CSS and frames is required (i.e. any modern browser). -# Windows users are probably better off using the HTML help feature. +# structure should be generated to display hierarchical information. If the tag +# value is set to YES, a side panel will be generated containing a tree-like +# index structure (just like the one that is generated for HTML Help). For this +# to work a browser that supports JavaScript, DHTML, CSS and frames is required +# (i.e. any modern browser). Windows users are probably better off using the +# HTML help feature. Via custom style sheets (see HTML_EXTRA_STYLESHEET) one can +# further fine tune the look of the index (see "Fine-tuning the output"). As an +# example, the default style sheet generated by doxygen has an example that +# shows how to put an image at the root of the tree instead of the PROJECT_NAME. +# Since the tree basically has the same information as the tab index, you could +# consider setting DISABLE_INDEX to YES when enabling this option. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_TREEVIEW = NO -# By enabling USE_INLINE_TREES, doxygen will generate the Groups, Directories, -# and Class Hierarchy pages using a tree view instead of an ordered list. +# When both GENERATE_TREEVIEW and DISABLE_INDEX are set to YES, then the +# FULL_SIDEBAR option determines if the side bar is limited to only the treeview +# area (value NO) or if it should extend to the full height of the window (value +# YES). Setting this to YES gives a layout similar to +# https://docs.readthedocs.io with more room for contents, but less room for the +# project logo, title, and description. If either GENERATE_TREEVIEW or +# DISABLE_INDEX is set to NO, this option has no effect. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. -USE_INLINE_TREES = NO +FULL_SIDEBAR = NO -# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be -# used to set the initial width (in pixels) of the frame in which the tree -# is shown. +# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that +# doxygen will group on one line in the generated HTML documentation. +# +# Note that a value of 0 will completely suppress the enum values from appearing +# in the overview section. +# Minimum value: 0, maximum value: 20, default value: 4. +# This tag requires that the tag GENERATE_HTML is set to YES. + +ENUM_VALUES_PER_LINE = 4 + +# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used +# to set the initial width (in pixels) of the frame in which the tree is shown. +# Minimum value: 0, maximum value: 1500, default value: 250. +# This tag requires that the tag GENERATE_HTML is set to YES. TREEVIEW_WIDTH = 250 -# When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open -# links to external symbols imported via tag files in a separate window. +# If the EXT_LINKS_IN_WINDOW option is set to YES, doxygen will open links to +# external symbols imported via tag files in a separate window. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. EXT_LINKS_IN_WINDOW = NO -# Use this tag to change the font size of Latex formulas included -# as images in the HTML documentation. The default is 10. Note that -# when you change the font size after a successful doxygen run you need -# to manually remove any form_*.png images from the HTML output directory -# to force them to be regenerated. +# If the OBFUSCATE_EMAILS tag is set to YES, doxygen will obfuscate email +# addresses. +# The default value is: YES. +# This tag requires that the tag GENERATE_HTML is set to YES. + +OBFUSCATE_EMAILS = YES + +# If the HTML_FORMULA_FORMAT option is set to svg, doxygen will use the pdf2svg +# tool (see https://github.com/dawbarton/pdf2svg) or inkscape (see +# https://inkscape.org) to generate formulas as SVG images instead of PNGs for +# the HTML output. These images will generally look nicer at scaled resolutions. +# Possible values are: png (the default) and svg (looks nicer but requires the +# pdf2svg or inkscape tool). +# The default value is: png. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_FORMULA_FORMAT = png + +# Use this tag to change the font size of LaTeX formulas included as images in +# the HTML documentation. When you change the font size after a successful +# doxygen run you need to manually remove any form_*.png images from the HTML +# output directory to force them to be regenerated. +# Minimum value: 8, maximum value: 50, default value: 10. +# This tag requires that the tag GENERATE_HTML is set to YES. FORMULA_FONTSIZE = 10 -# Use the FORMULA_TRANPARENT tag to determine whether or not the images -# generated for formulas are transparent PNGs. Transparent PNGs are -# not supported properly for IE 6.0, but are supported on all modern browsers. -# Note that when changing this option you need to delete any form_*.png files -# in the HTML output before the changes have effect. +# The FORMULA_MACROFILE can contain LaTeX \newcommand and \renewcommand commands +# to create new LaTeX commands to be used in formulas as building blocks. See +# the section "Including formulas" for details. -FORMULA_TRANSPARENT = YES +FORMULA_MACROFILE = -# When the SEARCHENGINE tag is enabled doxygen will generate a search box -# for the HTML output. The underlying search engine uses javascript -# and DHTML and should work on any modern browser. Note that when using -# HTML help (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets -# (GENERATE_DOCSET) there is already a search function so this one should -# typically be disabled. For large projects the javascript based search engine -# can be slow, then enabling SERVER_BASED_SEARCH may provide a better solution. +# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see +# https://www.mathjax.org) which uses client side JavaScript for the rendering +# instead of using pre-rendered bitmaps. Use this if you do not have LaTeX +# installed or if you want to formulas look prettier in the HTML output. When +# enabled you may also need to install MathJax separately and configure the path +# to it using the MATHJAX_RELPATH option. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +USE_MATHJAX = NO + +# With MATHJAX_VERSION it is possible to specify the MathJax version to be used. +# Note that the different versions of MathJax have different requirements with +# regards to the different settings, so it is possible that also other MathJax +# settings have to be changed when switching between the different MathJax +# versions. +# Possible values are: MathJax_2 and MathJax_3. +# The default value is: MathJax_2. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_VERSION = MathJax_2 + +# When MathJax is enabled you can set the default output format to be used for +# the MathJax output. For more details about the output format see MathJax +# version 2 (see: +# http://docs.mathjax.org/en/v2.7-latest/output.html) and MathJax version 3 +# (see: +# http://docs.mathjax.org/en/latest/web/components/output.html). +# Possible values are: HTML-CSS (which is slower, but has the best +# compatibility. This is the name for Mathjax version 2, for MathJax version 3 +# this will be translated into chtml), NativeMML (i.e. MathML. Only supported +# for NathJax 2. For MathJax version 3 chtml will be used instead.), chtml (This +# is the name for Mathjax version 3, for MathJax version 2 this will be +# translated into HTML-CSS) and SVG. +# The default value is: HTML-CSS. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_FORMAT = HTML-CSS + +# When MathJax is enabled you need to specify the location relative to the HTML +# output directory using the MATHJAX_RELPATH option. The destination directory +# should contain the MathJax.js script. For instance, if the mathjax directory +# is located at the same level as the HTML output directory, then +# MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax +# Content Delivery Network so you can quickly see the result without installing +# MathJax. However, it is strongly recommended to install a local copy of +# MathJax from https://www.mathjax.org before deployment. The default value is: +# - in case of MathJax version 2: https://cdn.jsdelivr.net/npm/mathjax@2 +# - in case of MathJax version 3: https://cdn.jsdelivr.net/npm/mathjax@3 +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_RELPATH = https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.5/ + +# The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax +# extension names that should be enabled during MathJax rendering. For example +# for MathJax version 2 (see +# https://docs.mathjax.org/en/v2.7-latest/tex.html#tex-and-latex-extensions): +# MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols +# For example for MathJax version 3 (see +# http://docs.mathjax.org/en/latest/input/tex/extensions/index.html): +# MATHJAX_EXTENSIONS = ams +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_EXTENSIONS = + +# The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces +# of code that will be used on startup of the MathJax code. See the MathJax site +# (see: +# http://docs.mathjax.org/en/v2.7-latest/output.html) for more details. For an +# example see the documentation. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_CODEFILE = + +# When the SEARCHENGINE tag is enabled doxygen will generate a search box for +# the HTML output. The underlying search engine uses javascript and DHTML and +# should work on any modern browser. Note that when using HTML help +# (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET) +# there is already a search function so this one should typically be disabled. +# For large projects the javascript based search engine can be slow, then +# enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to +# search using the keyboard; to jump to the search box use + S +# (what the is depends on the OS and browser, but it is typically +# , /