From 5133887632b9b36de032910429c04b843e9e4424 Mon Sep 17 00:00:00 2001 From: Adam Honse Date: Thu, 22 Jan 2026 13:02:56 -0600 Subject: [PATCH] Add optional HID device hotplug support (requires https://gitlab.com/OpenRGBDevelopers/hidapi-hotplug) --- .gitlab-ci.yml | 33 +- .../AOCKeyboardController.cpp | 2 +- .../AOCMouseController/AOCMouseController.cpp | 2 +- .../AOCMousematController.cpp | 2 +- .../CreativeSoundBlasterXG6Controller.cpp | 2 +- .../FnaticStreakController.cpp | 2 +- .../HYTEKeyboardController.cpp | 2 +- .../HyperXAlloyElite2Controller.cpp | 2 +- .../HyperXAlloyOrigins60and65Controller.cpp | 2 +- .../HyperXAlloyOriginsController.cpp | 2 +- .../HyperXAlloyOriginsCoreController.cpp | 2 +- .../HyperXOrigins2_65Controller.cpp | 2 +- .../HyperXMicrophoneController.cpp | 2 +- .../HyperXMicrophoneV2Controller.cpp | 2 +- .../HyperXPulsefireFPSProController.cpp | 2 +- .../HyperXPulsefireHasteController.cpp | 2 +- .../HyperXPulsefireSurgeController.cpp | 2 +- .../HyperXMousematController.cpp | 2 +- .../JGINYUEInternalUSBController.cpp | 2 +- .../JGINYUEInternalUSBV2Controller.cpp | 2 +- .../PatriotViperMouseController.cpp | 2 +- .../WushiController/WushiL50USBController.cpp | 4 +- .../ZETBladeOpticalController.cpp | 2 +- DetectionManager.cpp | 221 ++++- DetectionManager.h | 42 + OpenRGB.pro | 93 +- RGBController/RGBController.h | 6 + debian/control | 2 +- .../hidapi-hotplug-win/include/hidapi.h | 797 ++++++++++++++++++ .../include/hidapi_winapi.h | 94 +++ .../hidapi-hotplug-win/x64/hidapi-hotplug.dll | Bin 0 -> 45056 bytes .../hidapi-hotplug-win/x64/hidapi-hotplug.lib | Bin 0 -> 8500 bytes .../hidapi-hotplug-win/x86/hidapi-hotplug.dll | Bin 0 -> 35328 bytes .../hidapi-hotplug-win/x86/hidapi-hotplug.lib | Bin 0 -> 8640 bytes fedora/OpenRGB.spec.in | 2 +- hidapi_wrapper/hidapi_wrapper.h | 57 +- .../OpenRGBSoftwareInfoPage.cpp | 5 + .../OpenRGBSoftwareInfoPage.ui | 216 ++--- scripts/build-appimage.sh | 11 +- 39 files changed, 1407 insertions(+), 218 deletions(-) create mode 100644 dependencies/hidapi-hotplug-win/include/hidapi.h create mode 100644 dependencies/hidapi-hotplug-win/include/hidapi_winapi.h create mode 100644 dependencies/hidapi-hotplug-win/x64/hidapi-hotplug.dll create mode 100644 dependencies/hidapi-hotplug-win/x64/hidapi-hotplug.lib create mode 100644 dependencies/hidapi-hotplug-win/x86/hidapi-hotplug.dll create mode 100644 dependencies/hidapi-hotplug-win/x86/hidapi-hotplug.lib diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index ed6d75f78..f162e9a8e 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -296,7 +296,10 @@ before_script: image: fedora:43 stage: build script: - - dnf install rpmdevtools dnf-plugins-core libcurl-devel qt5-qtbase-devel git -y + - dnf install rpmdevtools dnf-plugins-core libcurl-devel qt5-qtbase-devel git wget -y + - 'wget -O hidapi-hotplug.zip https://gitlab.com/OpenRGBDevelopers/hidapi-hotplug/-/jobs/artifacts/master/download?job=Linux+64+F43+rpm' + - unzip hidapi-hotplug.zip + - dnf install ./*.rpm -y - rpmdev-setuptree - ./scripts/build-package-files.sh fedora/OpenRGB.spec - cp fedora/OpenRGB.spec /root/rpmbuild/SPECS @@ -324,10 +327,14 @@ before_script: image: i386/debian:bookworm stage: test script: - - apt update + - DEBIAN_FRONTEND=noninteractive apt update + - DEBIAN_FRONTEND=noninteractive apt install -y unzip curl + - 'curl -o hidapi-hotplug.zip -L https://gitlab.com/OpenRGBDevelopers/hidapi-hotplug/-/jobs/artifacts/master/download?job=Linux+i386+.deb+%28Debian+Bookworm%29' + - unzip hidapi-hotplug.zip + - DEBIAN_FRONTEND=noninteractive apt install -yq --no-install-recommends ./libhidapi-hotplug*.deb - DEBIAN_FRONTEND=noninteractive apt install -yq --no-install-recommends ./openrgb*i386.deb - openrgb --version - - apt remove -y openrgb + - DEBIAN_FRONTEND=noninteractive apt remove -y openrgb dependencies: - "Linux i386 .deb (Debian Bookworm)" needs: @@ -342,10 +349,14 @@ before_script: image: amd64/debian:bookworm stage: test script: - - apt update + - DEBIAN_FRONTEND=noninteractive apt update + - DEBIAN_FRONTEND=noninteractive apt install -y unzip curl + - 'curl -o hidapi-hotplug.zip -L https://gitlab.com/OpenRGBDevelopers/hidapi-hotplug/-/jobs/artifacts/master/download?job=Linux+amd64+.deb+%28Debian+Bookworm%29' + - unzip hidapi-hotplug.zip + - DEBIAN_FRONTEND=noninteractive apt install -yq --no-install-recommends ./libhidapi-hotplug*.deb - DEBIAN_FRONTEND=noninteractive apt install -yq --no-install-recommends ./openrgb*amd64.deb - openrgb --version - - apt remove -y openrgb + - DEBIAN_FRONTEND=noninteractive apt remove -y openrgb dependencies: - "Linux amd64 .deb (Debian Bookworm)" needs: @@ -360,10 +371,14 @@ before_script: image: ubuntu:noble stage: test script: - - apt update + - DEBIAN_FRONTEND=noninteractive apt update + - DEBIAN_FRONTEND=noninteractive apt install -y unzip curl + - 'curl -o hidapi-hotplug.zip -L https://gitlab.com/OpenRGBDevelopers/hidapi-hotplug/-/jobs/artifacts/master/download?job=Linux+amd64+.deb+%28Debian+Bookworm%29' + - unzip hidapi-hotplug.zip + - DEBIAN_FRONTEND=noninteractive apt install -yq --no-install-recommends ./libhidapi-hotplug*.deb - DEBIAN_FRONTEND=noninteractive apt install -yq --no-install-recommends ./openrgb*amd64.deb - openrgb --version - - apt remove -y openrgb + - DEBIAN_FRONTEND=noninteractive apt remove -y openrgb dependencies: - "Linux amd64 .deb (Debian Bookworm)" needs: @@ -378,6 +393,10 @@ before_script: image: fedora:43 stage: test script: + - dnf install unzip wget -y + - 'wget -O hidapi-hotplug.zip https://gitlab.com/OpenRGBDevelopers/hidapi-hotplug/-/jobs/artifacts/master/download?job=Linux+64+F43+rpm' + - unzip hidapi-hotplug.zip + - dnf install ./*.rpm -y - dnf5 -y install ./openrgb*64.rpm - openrgb --version - dnf5 -y remove openrgb diff --git a/Controllers/AOCKeyboardController/AOCKeyboardController.cpp b/Controllers/AOCKeyboardController/AOCKeyboardController.cpp index 96833370d..c05f798a0 100644 --- a/Controllers/AOCKeyboardController/AOCKeyboardController.cpp +++ b/Controllers/AOCKeyboardController/AOCKeyboardController.cpp @@ -31,7 +31,7 @@ AOCKeyboardController::~AOCKeyboardController() std::string AOCKeyboardController::GetDeviceLocation() { - return("HID " + location); + return("HID: " + location); } std::string AOCKeyboardController::GetDeviceName() diff --git a/Controllers/AOCMouseController/AOCMouseController.cpp b/Controllers/AOCMouseController/AOCMouseController.cpp index 15df00457..3f6ce7602 100644 --- a/Controllers/AOCMouseController/AOCMouseController.cpp +++ b/Controllers/AOCMouseController/AOCMouseController.cpp @@ -27,7 +27,7 @@ AOCMouseController::~AOCMouseController() std::string AOCMouseController::GetDeviceLocation() { - return("HID " + location); + return("HID: " + location); } std::string AOCMouseController::GetDeviceName() diff --git a/Controllers/AOCMousematController/AOCMousematController.cpp b/Controllers/AOCMousematController/AOCMousematController.cpp index 3c033481e..517e1a651 100644 --- a/Controllers/AOCMousematController/AOCMousematController.cpp +++ b/Controllers/AOCMousematController/AOCMousematController.cpp @@ -27,7 +27,7 @@ AOCMousematController::~AOCMousematController() std::string AOCMousematController::GetDeviceLocation() { - return("HID " + location); + return("HID: " + location); } std::string AOCMousematController::GetDeviceName() diff --git a/Controllers/CreativeController/CreativeSoundBlasterXG6Controller.cpp b/Controllers/CreativeController/CreativeSoundBlasterXG6Controller.cpp index 0fa333451..d69c880f0 100644 --- a/Controllers/CreativeController/CreativeSoundBlasterXG6Controller.cpp +++ b/Controllers/CreativeController/CreativeSoundBlasterXG6Controller.cpp @@ -23,7 +23,7 @@ CreativeSoundBlasterXG6Controller::~CreativeSoundBlasterXG6Controller() std::string CreativeSoundBlasterXG6Controller::GetDeviceLocation() { - return("HID " + location); + return("HID: " + location); } std::string CreativeSoundBlasterXG6Controller::GetDeviceName() diff --git a/Controllers/FnaticStreakController/FnaticStreakController.cpp b/Controllers/FnaticStreakController/FnaticStreakController.cpp index 5b2ac94b0..de06becd0 100644 --- a/Controllers/FnaticStreakController/FnaticStreakController.cpp +++ b/Controllers/FnaticStreakController/FnaticStreakController.cpp @@ -44,7 +44,7 @@ FnaticStreakController::~FnaticStreakController() std::string FnaticStreakController::GetDeviceLocation() { - return("HID " + location); + return("HID: " + location); } std::string FnaticStreakController::GetNameString() diff --git a/Controllers/HYTEKeyboardController/HYTEKeyboardController.cpp b/Controllers/HYTEKeyboardController/HYTEKeyboardController.cpp index e3a2daee6..61ab16a52 100644 --- a/Controllers/HYTEKeyboardController/HYTEKeyboardController.cpp +++ b/Controllers/HYTEKeyboardController/HYTEKeyboardController.cpp @@ -26,7 +26,7 @@ HYTEKeyboardController::~HYTEKeyboardController() std::string HYTEKeyboardController::GetDeviceLocation() { - return("HID " + location); + return("HID: " + location); } std::string HYTEKeyboardController::GetDeviceName() diff --git a/Controllers/HyperXKeyboardController/HyperXAlloyElite2Controller/HyperXAlloyElite2Controller.cpp b/Controllers/HyperXKeyboardController/HyperXAlloyElite2Controller/HyperXAlloyElite2Controller.cpp index b108eb865..379157ee6 100644 --- a/Controllers/HyperXKeyboardController/HyperXAlloyElite2Controller/HyperXAlloyElite2Controller.cpp +++ b/Controllers/HyperXKeyboardController/HyperXAlloyElite2Controller/HyperXAlloyElite2Controller.cpp @@ -32,7 +32,7 @@ HyperXAlloyElite2Controller::~HyperXAlloyElite2Controller() std::string HyperXAlloyElite2Controller::GetDeviceLocation() { - return("HID " + location); + return("HID: " + location); } std::string HyperXAlloyElite2Controller::GetNameString() diff --git a/Controllers/HyperXKeyboardController/HyperXAlloyOrigins60and65Controller/HyperXAlloyOrigins60and65Controller.cpp b/Controllers/HyperXKeyboardController/HyperXAlloyOrigins60and65Controller/HyperXAlloyOrigins60and65Controller.cpp index dd1ad2501..1db44a32b 100644 --- a/Controllers/HyperXKeyboardController/HyperXAlloyOrigins60and65Controller/HyperXAlloyOrigins60and65Controller.cpp +++ b/Controllers/HyperXKeyboardController/HyperXAlloyOrigins60and65Controller/HyperXAlloyOrigins60and65Controller.cpp @@ -27,7 +27,7 @@ HyperXAlloyOrigins60and65Controller::~HyperXAlloyOrigins60and65Controller() std::string HyperXAlloyOrigins60and65Controller::GetDeviceLocation() { - return("HID " + location); + return("HID: " + location); } std::string HyperXAlloyOrigins60and65Controller::GetNameString() diff --git a/Controllers/HyperXKeyboardController/HyperXAlloyOriginsController/HyperXAlloyOriginsController.cpp b/Controllers/HyperXKeyboardController/HyperXAlloyOriginsController/HyperXAlloyOriginsController.cpp index 742e215f1..e8eade6e3 100644 --- a/Controllers/HyperXKeyboardController/HyperXAlloyOriginsController/HyperXAlloyOriginsController.cpp +++ b/Controllers/HyperXKeyboardController/HyperXAlloyOriginsController/HyperXAlloyOriginsController.cpp @@ -30,7 +30,7 @@ HyperXAlloyOriginsController::~HyperXAlloyOriginsController() std::string HyperXAlloyOriginsController::GetDeviceLocation() { - return("HID " + location); + return("HID: " + location); } std::string HyperXAlloyOriginsController::GetNameString() diff --git a/Controllers/HyperXKeyboardController/HyperXAlloyOriginsCoreController/HyperXAlloyOriginsCoreController.cpp b/Controllers/HyperXKeyboardController/HyperXAlloyOriginsCoreController/HyperXAlloyOriginsCoreController.cpp index 554c82477..fb2a529c3 100644 --- a/Controllers/HyperXKeyboardController/HyperXAlloyOriginsCoreController/HyperXAlloyOriginsCoreController.cpp +++ b/Controllers/HyperXKeyboardController/HyperXAlloyOriginsCoreController/HyperXAlloyOriginsCoreController.cpp @@ -42,7 +42,7 @@ HyperXAlloyOriginsCoreController::~HyperXAlloyOriginsCoreController() std::string HyperXAlloyOriginsCoreController::GetDeviceLocation() { - return("HID " + location); + return("HID: " + location); } std::string HyperXAlloyOriginsCoreController::GetNameString() diff --git a/Controllers/HyperXKeyboardController/HyperXOrigins2_65Controller/HyperXOrigins2_65Controller.cpp b/Controllers/HyperXKeyboardController/HyperXOrigins2_65Controller/HyperXOrigins2_65Controller.cpp index bd5a9527d..21362e89d 100644 --- a/Controllers/HyperXKeyboardController/HyperXOrigins2_65Controller/HyperXOrigins2_65Controller.cpp +++ b/Controllers/HyperXKeyboardController/HyperXOrigins2_65Controller/HyperXOrigins2_65Controller.cpp @@ -27,7 +27,7 @@ HyperXOrigins2_65Controller::~HyperXOrigins2_65Controller() std::string HyperXOrigins2_65Controller::GetDeviceLocation() { - return("HID " + location); + return("HID: " + location); } std::string HyperXOrigins2_65Controller::GetNameString() diff --git a/Controllers/HyperXMicrophoneController/HyperXMicrophoneController.cpp b/Controllers/HyperXMicrophoneController/HyperXMicrophoneController.cpp index 458d3e5b6..b501e3240 100644 --- a/Controllers/HyperXMicrophoneController/HyperXMicrophoneController.cpp +++ b/Controllers/HyperXMicrophoneController/HyperXMicrophoneController.cpp @@ -37,7 +37,7 @@ HyperXMicrophoneController::~HyperXMicrophoneController() std::string HyperXMicrophoneController::GetDeviceLocation() { - return(location); + return("HID: " + location); } std::string HyperXMicrophoneController::GetNameString() diff --git a/Controllers/HyperXMicrophoneV2Controller/HyperXMicrophoneV2Controller.cpp b/Controllers/HyperXMicrophoneV2Controller/HyperXMicrophoneV2Controller.cpp index 9f93a2bc6..d88d4bc87 100644 --- a/Controllers/HyperXMicrophoneV2Controller/HyperXMicrophoneV2Controller.cpp +++ b/Controllers/HyperXMicrophoneV2Controller/HyperXMicrophoneV2Controller.cpp @@ -37,7 +37,7 @@ HyperXMicrophoneV2Controller::~HyperXMicrophoneV2Controller() std::string HyperXMicrophoneV2Controller::GetDeviceLocation() { - return(location); + return("HID: " + location); } std::string HyperXMicrophoneV2Controller::GetNameString() diff --git a/Controllers/HyperXMouseController/HyperXPulsefireFPSProController/HyperXPulsefireFPSProController.cpp b/Controllers/HyperXMouseController/HyperXPulsefireFPSProController/HyperXPulsefireFPSProController.cpp index 1907648a2..a0a54be48 100644 --- a/Controllers/HyperXMouseController/HyperXPulsefireFPSProController/HyperXPulsefireFPSProController.cpp +++ b/Controllers/HyperXMouseController/HyperXPulsefireFPSProController/HyperXPulsefireFPSProController.cpp @@ -27,7 +27,7 @@ HyperXPulsefireFPSProController::~HyperXPulsefireFPSProController() std::string HyperXPulsefireFPSProController::GetDeviceLocation() { - return("HID " + location); + return("HID: " + location); } std::string HyperXPulsefireFPSProController::GetNameString() diff --git a/Controllers/HyperXMouseController/HyperXPulsefireHasteController/HyperXPulsefireHasteController.cpp b/Controllers/HyperXMouseController/HyperXPulsefireHasteController/HyperXPulsefireHasteController.cpp index 23b2b5448..122d9bfb5 100644 --- a/Controllers/HyperXMouseController/HyperXPulsefireHasteController/HyperXPulsefireHasteController.cpp +++ b/Controllers/HyperXMouseController/HyperXPulsefireHasteController/HyperXPulsefireHasteController.cpp @@ -27,7 +27,7 @@ HyperXPulsefireHasteController::~HyperXPulsefireHasteController() std::string HyperXPulsefireHasteController::GetDeviceLocation() { - return("HID " + location); + return("HID: " + location); } std::string HyperXPulsefireHasteController::GetNameString() diff --git a/Controllers/HyperXMouseController/HyperXPulsefireSurgeController/HyperXPulsefireSurgeController.cpp b/Controllers/HyperXMouseController/HyperXPulsefireSurgeController/HyperXPulsefireSurgeController.cpp index 917cd46cb..b11cb310f 100644 --- a/Controllers/HyperXMouseController/HyperXPulsefireSurgeController/HyperXPulsefireSurgeController.cpp +++ b/Controllers/HyperXMouseController/HyperXPulsefireSurgeController/HyperXPulsefireSurgeController.cpp @@ -27,7 +27,7 @@ HyperXPulsefireSurgeController::~HyperXPulsefireSurgeController() std::string HyperXPulsefireSurgeController::GetDeviceLocation() { - return("HID " + location); + return("HID: " + location); } std::string HyperXPulsefireSurgeController::GetNameString() diff --git a/Controllers/HyperXMousematController/HyperXMousematController.cpp b/Controllers/HyperXMousematController/HyperXMousematController.cpp index 91320c8e6..97dd4e402 100644 --- a/Controllers/HyperXMousematController/HyperXMousematController.cpp +++ b/Controllers/HyperXMousematController/HyperXMousematController.cpp @@ -28,7 +28,7 @@ HyperXMousematController::~HyperXMousematController() std::string HyperXMousematController::GetDeviceLocation() { - return("HID " + location); + return("HID: " + location); } std::string HyperXMousematController::GetNameString() diff --git a/Controllers/JGINYUEInternalUSBController/JGINYUEInternalUSBController.cpp b/Controllers/JGINYUEInternalUSBController/JGINYUEInternalUSBController.cpp index 96f5207e6..2785afeae 100644 --- a/Controllers/JGINYUEInternalUSBController/JGINYUEInternalUSBController.cpp +++ b/Controllers/JGINYUEInternalUSBController/JGINYUEInternalUSBController.cpp @@ -54,7 +54,7 @@ unsigned int JGINYUEInternalUSBController::GetZoneCount() std::string JGINYUEInternalUSBController::GetDeviceLocation() { - return("HID:" + location); + return("HID: " + location); } std::string JGINYUEInternalUSBController::GetDeviceName() diff --git a/Controllers/JGINYUEInternalUSBV2Controller/JGINYUEInternalUSBV2Controller.cpp b/Controllers/JGINYUEInternalUSBV2Controller/JGINYUEInternalUSBV2Controller.cpp index 8d12a0afa..f42c47d23 100644 --- a/Controllers/JGINYUEInternalUSBV2Controller/JGINYUEInternalUSBV2Controller.cpp +++ b/Controllers/JGINYUEInternalUSBV2Controller/JGINYUEInternalUSBV2Controller.cpp @@ -68,7 +68,7 @@ unsigned int JGINYUEInternalUSBV2Controller::GetZoneCount() std::string JGINYUEInternalUSBV2Controller::GetDeviceLocation() { - return("HID:" + location); + return("HID: " + location); } std::string JGINYUEInternalUSBV2Controller::GetDeviceName() diff --git a/Controllers/PatriotViperMouseController/PatriotViperMouseController.cpp b/Controllers/PatriotViperMouseController/PatriotViperMouseController.cpp index f4fdbb507..dfdcb9881 100644 --- a/Controllers/PatriotViperMouseController/PatriotViperMouseController.cpp +++ b/Controllers/PatriotViperMouseController/PatriotViperMouseController.cpp @@ -28,7 +28,7 @@ PatriotViperMouseController::~PatriotViperMouseController() std::string PatriotViperMouseController::GetLocation() { - return("HID " + location); + return("HID: " + location); } std::string PatriotViperMouseController::GetName() diff --git a/Controllers/WushiController/WushiL50USBController.cpp b/Controllers/WushiController/WushiL50USBController.cpp index 45949a27f..6f017d1b1 100644 --- a/Controllers/WushiController/WushiL50USBController.cpp +++ b/Controllers/WushiController/WushiL50USBController.cpp @@ -26,12 +26,12 @@ WushiL50USBController::~WushiL50USBController() std::string WushiL50USBController::getName() { - return name; + return(name); } std::string WushiL50USBController::getLocation() { - return location; + return("HID: " + location); } std::string WushiL50USBController::GetSerialString() diff --git a/Controllers/ZETKeyboardController/ZETBladeOpticalController.cpp b/Controllers/ZETKeyboardController/ZETBladeOpticalController.cpp index 9467f3448..802a881fc 100644 --- a/Controllers/ZETKeyboardController/ZETBladeOpticalController.cpp +++ b/Controllers/ZETKeyboardController/ZETBladeOpticalController.cpp @@ -43,7 +43,7 @@ ZETBladeOpticalController::~ZETBladeOpticalController() std::string ZETBladeOpticalController::GetDeviceLocation() { - return("HID " + location); + return("HID: " + location); } std::string ZETBladeOpticalController::GetNameString() diff --git a/DetectionManager.cpp b/DetectionManager.cpp index b6803b886..e88e9b53e 100644 --- a/DetectionManager.cpp +++ b/DetectionManager.cpp @@ -76,14 +76,18 @@ const char* UDEV_MUTLI = QT_TRANSLATE_NOOP("DetectionManager", const hidapi_wrapper default_hidapi_wrapper = { NULL, - (hidapi_wrapper_send_feature_report) hid_send_feature_report, - (hidapi_wrapper_get_feature_report) hid_get_feature_report, - (hidapi_wrapper_get_serial_number_string) hid_get_serial_number_string, - (hidapi_wrapper_open_path) hid_open_path, - (hidapi_wrapper_enumerate) hid_enumerate, - (hidapi_wrapper_free_enumeration) hid_free_enumeration, - (hidapi_wrapper_close) hid_close, - (hidapi_wrapper_error) hid_error + (hidapi_wrapper_send_feature_report) hid_send_feature_report, + (hidapi_wrapper_get_feature_report) hid_get_feature_report, + (hidapi_wrapper_get_serial_number_string) hid_get_serial_number_string, + (hidapi_wrapper_open_path) hid_open_path, + (hidapi_wrapper_enumerate) hid_enumerate, + (hidapi_wrapper_free_enumeration) hid_free_enumeration, + (hidapi_wrapper_close) hid_close, + (hidapi_wrapper_error) hid_error, +#if(HID_HOTPLUG_ENABLED) + (hidapi_wrapper_hotplug_register_callback) hid_hotplug_register_callback, + (hidapi_wrapper_hotplug_deregister_callback) hid_hotplug_deregister_callback, +#endif }; /*---------------------------------------------------------*\ @@ -143,6 +147,12 @@ DetectionManager::DetectionManager() dynamic_detectors_processed = false; initial_detection = true; +#ifdef __linux__ +#ifdef __GLIBC__ + hidapi_libusb_handle = nullptr; +#endif +#endif + /*-----------------------------------------------------*\ | Start the background thread | \*-----------------------------------------------------*/ @@ -711,7 +721,12 @@ void DetectionManager::BackgroundDetectDevices() } else { +#if(HID_HOTPLUG_ENABLED) + StopHIDHotplug(); + StartHIDHotplug(); +#else BackgroundDetectHIDDevices(hid_devices, detector_settings); +#endif } /*-----------------------------------------------------*\ @@ -1141,31 +1156,12 @@ void DetectionManager::BackgroundDetectHIDDevicesWrapped(hid_device_info* hid_de LOG_INFO("| Detecting libusb HID devices |"); LOG_INFO("------------------------------------------------------"); - void * dyn_handle = NULL; - hidapi_wrapper wrapper; - /*-----------------------------------------------------*\ | Load the libhidapi-libusb library | \*-----------------------------------------------------*/ - if((dyn_handle = dlopen("libhidapi-libusb.so", RTLD_NOW | RTLD_NODELETE | RTLD_DEEPBIND))) + if(hidapi_libusb_handle) { - /*-------------------------------------------------*\ - | Create a wrapper with the libusb functions | - \*-------------------------------------------------*/ - wrapper = - { - .dyn_handle = dyn_handle, - .hid_send_feature_report = (hidapi_wrapper_send_feature_report) dlsym(dyn_handle,"hid_send_feature_report"), - .hid_get_feature_report = (hidapi_wrapper_get_feature_report) dlsym(dyn_handle,"hid_get_feature_report"), - .hid_get_serial_number_string = (hidapi_wrapper_get_serial_number_string) dlsym(dyn_handle,"hid_get_serial_number_string"), - .hid_open_path = (hidapi_wrapper_open_path) dlsym(dyn_handle,"hid_open_path"), - .hid_enumerate = (hidapi_wrapper_enumerate) dlsym(dyn_handle,"hid_enumerate"), - .hid_free_enumeration = (hidapi_wrapper_free_enumeration) dlsym(dyn_handle,"hid_free_enumeration"), - .hid_close = (hidapi_wrapper_close) dlsym(dyn_handle,"hid_close"), - .hid_error = (hidapi_wrapper_error) dlsym(dyn_handle,"hid_free_enumeration") - }; - - hid_devices = wrapper.hid_enumerate(0, 0); + hid_devices = hidapi_libusb_wrapper.hid_enumerate(0, 0); hid_device_info* current_hid_device = hid_devices; @@ -1177,7 +1173,7 @@ void DetectionManager::BackgroundDetectHIDDevicesWrapped(hid_device_info* hid_de while(current_hid_device) { - RunHIDWrappedDetector(&wrapper, current_hid_device, detector_settings); + RunHIDWrappedDetector(&hidapi_libusb_wrapper, current_hid_device, detector_settings); /*---------------------------------------------*\ | Update detection percent | @@ -1195,7 +1191,7 @@ void DetectionManager::BackgroundDetectHIDDevicesWrapped(hid_device_info* hid_de /*-------------------------------------------------*\ | Done using the device list, free it | \*-------------------------------------------------*/ - wrapper.hid_free_enumeration(hid_devices); + hidapi_libusb_wrapper.hid_free_enumeration(hid_devices); } } #endif @@ -1268,6 +1264,48 @@ void DetectionManager::BackgroundHIDInit() int hid_status = hid_init(); LOG_DEBUG("[%s] Initializing HID interfaces: %s", DETECTIONMANAGER, ((hid_status == 0) ? "Success" : "Failed")); + +#ifdef __linux__ +#ifdef __GLIBC__ + /*-----------------------------------------------------*\ + | Load the libhidapi-libusb library | + \*-----------------------------------------------------*/ +#if(HID_HOTPLUG_ENABLED) + hidapi_libusb_handle = dlopen("libhidapi-hotplug-libusb.so", RTLD_NOW | RTLD_NODELETE | RTLD_DEEPBIND); +#else + hidapi_libusb_handle = dlopen("libhidapi-libusb.so", RTLD_NOW | RTLD_NODELETE | RTLD_DEEPBIND); +#endif + + if(hidapi_libusb_handle) + { + LOG_DEBUG("[%s] Loaded libusb HID library", DETECTIONMANAGER); + + /*-------------------------------------------------*\ + | Create a wrapper with the libusb functions | + \*-------------------------------------------------*/ + hidapi_libusb_wrapper = + { + .dyn_handle = hidapi_libusb_handle, + .hid_send_feature_report = (hidapi_wrapper_send_feature_report) dlsym(hidapi_libusb_handle,"hid_send_feature_report"), + .hid_get_feature_report = (hidapi_wrapper_get_feature_report) dlsym(hidapi_libusb_handle,"hid_get_feature_report"), + .hid_get_serial_number_string = (hidapi_wrapper_get_serial_number_string) dlsym(hidapi_libusb_handle,"hid_get_serial_number_string"), + .hid_open_path = (hidapi_wrapper_open_path) dlsym(hidapi_libusb_handle,"hid_open_path"), + .hid_enumerate = (hidapi_wrapper_enumerate) dlsym(hidapi_libusb_handle,"hid_enumerate"), + .hid_free_enumeration = (hidapi_wrapper_free_enumeration) dlsym(hidapi_libusb_handle,"hid_free_enumeration"), + .hid_close = (hidapi_wrapper_close) dlsym(hidapi_libusb_handle,"hid_close"), + .hid_error = (hidapi_wrapper_error) dlsym(hidapi_libusb_handle,"hid_free_enumeration"), +#if(HID_HOTPLUG_ENABLED) + .hid_hotplug_register_callback = (hidapi_wrapper_hotplug_register_callback) dlsym(hidapi_libusb_handle,"hid_hotplug_register_callback"), + .hid_hotplug_deregister_callback = (hidapi_wrapper_hotplug_deregister_callback) dlsym(hidapi_libusb_handle,"hid_hotplug_deregister_callback"), +#endif + }; + } + else + { + LOG_DEBUG("[%s] Failed to load libusb HID library", DETECTIONMANAGER); + } +#endif +#endif } /*---------------------------------------------------------*\ @@ -1320,6 +1358,12 @@ void DetectionManager::RunHIDDetector(hid_device_info* current_hid_device, json for(std::size_t detected_controller_idx = 0; detected_controller_idx < detected_controllers.size(); detected_controller_idx++) { + detected_controllers[detected_controller_idx]->detection_path = std::string(current_hid_device->path); + +#if(HID_HOTPLUG_ENABLED) + int handle; + hid_hotplug_register_callback(current_hid_device->vendor_id, current_hid_device->product_id, HID_API_HOTPLUG_EVENT_DEVICE_LEFT, 0, &DetectionManager::UnplugCallbackFunction, detected_controllers[detected_controller_idx], &handle); +#endif RegisterRGBController(detected_controllers[detected_controller_idx]); } } @@ -1376,6 +1420,13 @@ void DetectionManager::RunHIDWrappedDetector(const hidapi_wrapper* wrapper, hid_ for(std::size_t detected_controller_idx = 0; detected_controller_idx < detected_controllers.size(); detected_controller_idx++) { + detected_controllers[detected_controller_idx]->detection_path = std::string(current_hid_device->path); + +#if(HID_HOTPLUG_ENABLED) + int handle; + + wrapper->hid_hotplug_register_callback(current_hid_device->vendor_id, current_hid_device->product_id, HID_API_HOTPLUG_EVENT_DEVICE_LEFT, 0, &DetectionManager::UnplugCallbackFunction, detected_controllers[detected_controller_idx], &handle); +#endif RegisterRGBController(detected_controllers[detected_controller_idx]); } } @@ -1392,6 +1443,10 @@ void DetectionManager::ProcessCleanup() { WaitForDetection(); +#if(HID_HOTPLUG_ENABLED) + StopHIDHotplug(); +#endif + /*-----------------------------------------------------*\ | Make a copy of the list so that the controllers can | | be deleted after the list is cleared | @@ -1619,6 +1674,110 @@ void DetectionManager::UpdateDetectorSettings() } } +#if(HID_HOTPLUG_ENABLED) +/*---------------------------------------------------------*\ +| HID hotplug management functions | +\*---------------------------------------------------------*/ +void DetectionManager::StartHIDHotplug() +{ + hid_hotplug_register_callback(0, 0, HID_API_HOTPLUG_EVENT_DEVICE_ARRIVED, HID_API_HOTPLUG_ENUMERATE, &DetectionManager::HotplugCallbackFunction, nullptr, &hotplug_callback_handle); + +#ifdef __linux__ +#ifdef __GLIBC__ + if(hidapi_libusb_handle != nullptr) + { + hidapi_libusb_wrapper.hid_hotplug_register_callback(0, 0, HID_API_HOTPLUG_EVENT_DEVICE_ARRIVED, HID_API_HOTPLUG_ENUMERATE, &DetectionManager::WrappedHotplugCallbackFunction, nullptr, &libusb_hotplug_callback_handle); + } +#endif +#endif +} + +void DetectionManager::StopHIDHotplug() +{ + hid_hotplug_deregister_callback(hotplug_callback_handle); + +#ifdef __linux__ +#ifdef __GLIBC__ + if(hidapi_libusb_handle != nullptr) + { + hidapi_libusb_wrapper.hid_hotplug_deregister_callback(libusb_hotplug_callback_handle); + } +#endif +#endif +} + +/*---------------------------------------------------------*\ +| HID hotplug callback functions | +\*---------------------------------------------------------*/ +int DetectionManager::HotplugCallbackFunction(hid_hotplug_callback_handle /*callback_handle*/, hid_device_info *device, hid_hotplug_event event, void * /*user_data*/) +{ + /*-----------------------------------------------------*\ + | Open device disable list and read in disabled | + | device strings | + \*-----------------------------------------------------*/ + json detector_settings = ResourceManager::get()->GetSettingsManager()->GetSettings("Detectors"); + DetectionManager* dm = DetectionManager::get(); + + if(event == HID_API_HOTPLUG_EVENT_DEVICE_ARRIVED) + { + LOG_INFO("[%s] HID device connected: [%04x:%04x - %s]", DETECTIONMANAGER, device->vendor_id, device->product_id, device->path); + + dm->RunHIDDetector(device, detector_settings); + dm->RunHIDWrappedDetector(&default_hidapi_wrapper, device, detector_settings); + } + + return 0; +} + +int DetectionManager::UnplugCallbackFunction(hid_hotplug_callback_handle /*callback_handle*/, hid_device_info *device, hid_hotplug_event event, void *user_data) +{ + DetectionManager* dm = DetectionManager::get(); + + if(event == HID_API_HOTPLUG_EVENT_DEVICE_LEFT) + { + LOG_INFO("[%s] HID device disconnected: [%04x:%04x - %s]", DETECTIONMANAGER, device->vendor_id, device->product_id, device->path); + + /*-------------------------------------------------*\ + | User data is the pointer to the controller being | + | removed | + \*-------------------------------------------------*/ + RGBController* controller = (RGBController*)(user_data); + + if(controller && controller->detection_path == std::string(device->path)) + { + dm->UnregisterRGBController(controller); + delete controller; + return 1; + } + } + return 0; +} + +#ifdef __linux__ +#ifdef __GLIBC__ +int DetectionManager::WrappedHotplugCallbackFunction(hid_hotplug_callback_handle /*callback_handle*/, hid_device_info *device, hid_hotplug_event event, void * /*user_data*/) +{ + /*-----------------------------------------------------*\ + | Open device disable list and read in disabled | + | device strings | + \*-----------------------------------------------------*/ + json detector_settings = ResourceManager::get()->GetSettingsManager()->GetSettings("Detectors"); + DetectionManager* dm = DetectionManager::get(); + + if(event == HID_API_HOTPLUG_EVENT_DEVICE_ARRIVED) + { + LOG_INFO("[%s] libusb HID device connected: [%04x:%04x - %s]", DETECTIONMANAGER, device->vendor_id, device->product_id, device->path); + + dm->RunHIDDetector(device, detector_settings); + dm->RunHIDWrappedDetector(&DetectionManager::get()->hidapi_libusb_wrapper, device, detector_settings); + } + + return 0; +} +#endif +#endif +#endif + /*---------------------------------------------------------*\ | Function for signalling DetectionManager updates to | | registered callbacks | diff --git a/DetectionManager.h b/DetectionManager.h index 49358bfec..d978d0067 100644 --- a/DetectionManager.h +++ b/DetectionManager.h @@ -228,6 +228,28 @@ private: std::vector dynamic_detector_strings; std::vector pre_detection_hooks; +#ifdef __linux__ +#ifdef __GLIBC__ + /*-----------------------------------------------------*\ + | Wrapped libusb hidapi handles | + \*-----------------------------------------------------*/ + void * hidapi_libusb_handle; + hidapi_wrapper hidapi_libusb_wrapper; +#endif +#endif + + /*-----------------------------------------------------*\ + | HID Hotplug handles | + \*-----------------------------------------------------*/ +#if(HID_HOTPLUG_ENABLED) + hid_hotplug_callback_handle hotplug_callback_handle; +#ifdef __linux__ +#ifdef __GLIBC__ + hid_hotplug_callback_handle libusb_hotplug_callback_handle; +#endif +#endif +#endif + /*-----------------------------------------------------*\ | Detection Callbacks | \*-----------------------------------------------------*/ @@ -303,6 +325,26 @@ private: void ProcessPreDetectionHooks(); void UpdateDetectorSettings(); +#if(HID_HOTPLUG_ENABLED) + /*-----------------------------------------------------*\ + | HID hotplug management functions | + \*-----------------------------------------------------*/ + void StartHIDHotplug(); + void StopHIDHotplug(); + + /*-----------------------------------------------------*\ + | HID hotplug callback functions | + \*-----------------------------------------------------*/ + static int HotplugCallbackFunction(hid_hotplug_callback_handle callback_handle, hid_device_info *device, hid_hotplug_event event, void *user_data); + static int UnplugCallbackFunction(hid_hotplug_callback_handle callback_handle, hid_device_info *device, hid_hotplug_event event, void *user_data); + +#ifdef __linux__ +#ifdef __GLIBC__ + static int WrappedHotplugCallbackFunction(hid_hotplug_callback_handle callback_handle, hid_device_info *device, hid_hotplug_event event, void *user_data); +#endif +#endif +#endif + /*-----------------------------------------------------*\ | Function for signalling DetectionManager updates to | | registered callbacks | diff --git a/OpenRGB.pro b/OpenRGB.pro index c9f798be8..8a0d66cd4 100644 --- a/OpenRGB.pro +++ b/OpenRGB.pro @@ -58,14 +58,6 @@ VERSION_RPM = $$VERSION_RPM"^"$$SUFFIX TARGET = OpenRGB TEMPLATE = app -message("VERSION_NUM: "$$VERSION_NUM) -message("VERSION_STR: "$$VERSION_STR) -message("VERSION_SFX: "$$SUFFIX) -message("VERSION_DEB: "$$VERSION_DEB) -message("VERSION_WIX: "$$VERSION_WIX) -message("VERSION_AUR: "$$VERSION_AUR) -message("VERSION_RPM: "$$VERSION_RPM) -message("QT_VERSION: "$$QT_VERSION) #-----------------------------------------------------------------------------------------------# # Automatically generated build information # #-----------------------------------------------------------------------------------------------# @@ -83,7 +75,6 @@ else { GIT_BRANCH = $$system(powershell -ExecutionPolicy Bypass -File scripts/git-get-branch.ps1) } -message("GIT_BRANCH: "$$GIT_BRANCH) DEFINES += \ VERSION_STRING=\\"\"\"$$VERSION_STR\\"\"\" \ BUILDDATE_STRING=\\"\"\"$$BUILDDATE\\"\"\" \ @@ -136,6 +127,8 @@ CONTROLLER_CPP -= $$CONTROLLER_CPP_LINUX CONTROLLER_CPP -= $$CONTROLLER_CPP_FREEBSD CONTROLLER_CPP -= $$CONTROLLER_CPP_MACOS +HID_HOTPLUG_ENABLED = "false" + #-----------------------------------------------------------------------------------------------# # OpenRGB Common # #-----------------------------------------------------------------------------------------------# @@ -328,7 +321,7 @@ TRANSLATIONS += win32:QMAKE_CXXFLAGS += /utf-8 win32:INCLUDEPATH += \ dependencies/display-library/include \ - dependencies/hidapi-win/include \ + dependencies/hidapi-hotplug-win/include \ dependencies/libusb-1.0.27/include \ dependencies/mbedtls-3.2.1/include \ dependencies/NVFC \ @@ -374,7 +367,7 @@ win32:contains(QMAKE_TARGET.arch, x86_64) { -lws2_32 \ -liphlpapi \ -L"$$PWD/dependencies/libusb-1.0.27/VS2019/MS64/dll" -llibusb-1.0 \ - -L"$$PWD/dependencies/hidapi-win/x64/" -lhidapi \ + -L"$$PWD/dependencies/hidapi-hotplug-win/x64/" -lhidapi-hotplug \ -L"$$PWD/dependencies/mbedtls-3.2.1/lib/x64/" -lmbedcrypto -lmbedtls -lmbedx509 \ -L"$$PWD/dependencies/PawnIO/" -lPawnIOLib \ } @@ -387,7 +380,7 @@ win32:contains(QMAKE_TARGET.arch, x86) { -lws2_32 \ -liphlpapi \ -L"$$PWD/dependencies/libusb-1.0.27/VS2019/MS32/dll" -llibusb-1.0 \ - -L"$$PWD/dependencies/hidapi-win/x86/" -lhidapi \ + -L"$$PWD/dependencies/hidapi-hotplug-win/x86/" -lhidapi-hotplug \ -L"$$PWD/dependencies/mbedtls-3.2.1/lib/x86/" -lmbedcrypto -lmbedtls -lmbedx509 \ } @@ -401,6 +394,9 @@ win32:DEFINES += _CRT_SECURE_NO_WARNINGS \ _WINSOCK_DEPRECATED_NO_WARNINGS \ WIN32_LEAN_AND_MEAN \ + HID_HOTPLUG_ENABLED=1 \ + +win32:HID_HOTPLUG_ENABLED = "true" win32:RC_ICONS += \ qt/OpenRGB.ico @@ -432,7 +428,7 @@ win32:UI_DIR = _intermediate_$$DESTDIR/.ui win32:contains(QMAKE_TARGET.arch, x86_64) { copydata.commands += $(COPY_FILE) \"$$shell_path($$PWD/dependencies/libusb-1.0.27/VS2019/MS64/dll/libusb-1.0.dll)\" \"$$shell_path($$DESTDIR)\" $$escape_expand(\n\t) - copydata.commands += $(COPY_FILE) \"$$shell_path($$PWD/dependencies/hidapi-win/x64/hidapi.dll )\" \"$$shell_path($$DESTDIR)\" $$escape_expand(\n\t) + copydata.commands += $(COPY_FILE) \"$$shell_path($$PWD/dependencies/hidapi-hotplug-win/x64/hidapi-hotplug.dll )\" \"$$shell_path($$DESTDIR)\" $$escape_expand(\n\t) copydata.commands += $(COPY_FILE) \"$$shell_path($$PWD/dependencies/PawnIO/PawnIOLib.dll )\" \"$$shell_path($$DESTDIR)\" $$escape_expand(\n\t) copydata.commands += $(COPY_FILE) \"$$shell_path($$PWD/dependencies/PawnIO/modules/SmbusPIIX4.bin )\" \"$$shell_path($$DESTDIR)\" $$escape_expand(\n\t) copydata.commands += $(COPY_FILE) \"$$shell_path($$PWD/dependencies/PawnIO/modules/SmbusI801.bin )\" \"$$shell_path($$DESTDIR)\" $$escape_expand(\n\t) @@ -446,7 +442,7 @@ win32:contains(QMAKE_TARGET.arch, x86_64) { win32:contains(QMAKE_TARGET.arch, x86) { copydata.commands += $(COPY_FILE) \"$$shell_path($$PWD/dependencies/libusb-1.0.27/VS2019/MS32/dll/libusb-1.0.dll)\" \"$$shell_path($$DESTDIR)\" $$escape_expand(\n\t) - copydata.commands += $(COPY_FILE) \"$$shell_path($$PWD/dependencies/hidapi-win/x86/hidapi.dll )\" \"$$shell_path($$DESTDIR)\" $$escape_expand(\n\t) + copydata.commands += $(COPY_FILE) \"$$shell_path($$PWD/dependencies/hidapi-hotplug-win/x86/hidapi-hotplug.dll )\" \"$$shell_path($$DESTDIR)\" $$escape_expand(\n\t) first.depends = $(first) copydata export(first.depends) @@ -501,22 +497,29 @@ contains(QMAKE_PLATFORM, linux) { # Determine which hidapi to use based on availability # # Prefer hidraw backend, then libusb # #-------------------------------------------------------------------------------------------# - packagesExist(hidapi-hidraw) { - PKGCONFIG += hidapi-hidraw - - #---------------------------------------------------------------------------------------# - # hidapi-hidraw >= 0.10.1 supports USAGE/USAGE_PAGE # - # Define USE_HID_USAGE if hidapi-hidraw supports it # - #---------------------------------------------------------------------------------------# - HIDAPI_HIDRAW_VERSION = $$system($$PKG_CONFIG --modversion hidapi-hidraw) - if(versionAtLeast(HIDAPI_HIDRAW_VERSION, "0.10.1")) { - DEFINES += USE_HID_USAGE - } + packagesExist(hidapi-hotplug-hidraw) { + PKGCONFIG += hidapi-hotplug-hidraw + DEFINES += USE_HID_USAGE=1 \ + HID_HOTPLUG_ENABLED=1 + HID_HOTPLUG_ENABLED = "true" } else { - packagesExist(hidapi-libusb) { - PKGCONFIG += hidapi-libusb + packagesExist(hidapi-hidraw) { + PKGCONFIG += hidapi-hidraw + + #-----------------------------------------------------------------------------------# + # hidapi-hidraw >= 0.10.1 supports USAGE/USAGE_PAGE # + # Define USE_HID_USAGE if hidapi-hidraw supports it # + #-----------------------------------------------------------------------------------# + HIDAPI_HIDRAW_VERSION = $$system($$PKG_CONFIG --modversion hidapi-hidraw) + if(versionAtLeast(HIDAPI_HIDRAW_VERSION, "0.10.1")) { + DEFINES += USE_HID_USAGE + } } else { - PKGCONFIG += hidapi + packagesExist(hidapi-libusb) { + PKGCONFIG += hidapi-libusb + } else { + PKGCONFIG += hidapi + } } } @@ -711,7 +714,6 @@ macx { PKGCONFIG += \ libusb-1.0 \ - hidapi DEFINES += \ USE_HID_USAGE \ @@ -736,6 +738,21 @@ macx { SOURCES += $$CONTROLLER_CPP_MACOS + #-------------------------------------------------------------------------------------------# + # Determine which hidapi to use based on availability # + # Prefer hidapi-hotplug if it exists # + #-------------------------------------------------------------------------------------------# + packagesExist(hidapi-hotplug) { + PKGCONFIG += hidapi-hotplug + DEFINES += USE_HID_USAGE=1 \ + HID_HOTPLUG_ENABLED=1 + HID_HOTPLUG_ENABLED = "true" + + } else { + PKGCONFIG += hidapi + DEFINES += USE_HID_USAGE=1 + } + # Use mbedtls 3 MBEDTLS_PREFIX = $$system(brew --prefix mbedtls@3) @@ -809,3 +826,21 @@ macx:contains(QMAKE_HOST.arch, x86_64) { DISTFILES += \ debian/openrgb-udev.postinst \ debian/openrgb.postinst + +#-----------------------------------------------------------------------------------------------# +# Print build configuration # +#-----------------------------------------------------------------------------------------------# +message("GIT_BRANCH: "$$GIT_BRANCH) +message("VERSION_NUM: "$$VERSION_NUM) +message("VERSION_STR: "$$VERSION_STR) +message("VERSION_SFX: "$$SUFFIX) +message("VERSION_DEB: "$$VERSION_DEB) +message("VERSION_WIX: "$$VERSION_WIX) +message("VERSION_AUR: "$$VERSION_AUR) +message("VERSION_RPM: "$$VERSION_RPM) +message("QT_VERSION: "$$QT_VERSION) +equals(HID_HOTPLUG_ENABLED, "true") { +message("HID Hotplug: Enabled") +} else { +message("HID Hotplug: Disabled") +} diff --git a/RGBController/RGBController.h b/RGBController/RGBController.h index f0d5fc036..bbde3ed42 100644 --- a/RGBController/RGBController.h +++ b/RGBController/RGBController.h @@ -751,6 +751,12 @@ private: std::vector UpdateCallbacks; std::vector UpdateCallbackArgs; + /*-----------------------------------------------------*\ + | Private path used internally by DetectionManager | + | for HID hotplug path tracking | + \*-----------------------------------------------------*/ + std::string detection_path; + /*-----------------------------------------------------*\ | Certain internal OpenRGB framework classes can modify | | protected members | diff --git a/debian/control b/debian/control index 7f0335fab..092c7e98f 100644 --- a/debian/control +++ b/debian/control @@ -11,7 +11,7 @@ Build-Depends: qt6-tools-dev-tools, qt5-qmake, libusb-1.0-0-dev, - libhidapi-dev, + libhidapi-hotplug-dev, libmbedtls-dev, Homepage: https://gitlab.com/CalcProgrammer1/OpenRGB diff --git a/dependencies/hidapi-hotplug-win/include/hidapi.h b/dependencies/hidapi-hotplug-win/include/hidapi.h new file mode 100644 index 000000000..d7e89aabe --- /dev/null +++ b/dependencies/hidapi-hotplug-win/include/hidapi.h @@ -0,0 +1,797 @@ +/******************************************************* + HIDAPI - Multi-Platform library for + communication with HID devices. + + Alan Ott + Signal 11 Software + + libusb/hidapi Team + + Copyright 2023, All Rights Reserved. + + At the discretion of the user of this library, + this software may be licensed under the terms of the + GNU General Public License v3, a BSD-Style license, or the + original HIDAPI license as outlined in the LICENSE.txt, + LICENSE-gpl3.txt, LICENSE-bsd.txt, and LICENSE-orig.txt + files located at the root of the source distribution. + These files may also be found in the public source + code repository located at: + https://github.com/libusb/hidapi . +********************************************************/ + +/** @file + * @defgroup API hidapi API + */ + +#ifndef HIDAPI_H__ +#define HIDAPI_H__ + +#include + +/* #480: this is to be refactored properly for v1.0 */ +#ifdef _WIN32 + #ifndef HID_API_NO_EXPORT_DEFINE + #define HID_API_EXPORT __declspec(dllexport) + #endif +#endif +#ifndef HID_API_EXPORT + #define HID_API_EXPORT /**< API export macro */ +#endif +/* To be removed in v1.0 */ +#define HID_API_CALL /**< API call macro */ + +#define HID_API_EXPORT_CALL HID_API_EXPORT HID_API_CALL /**< API export and call macro*/ + +/** @brief Static/compile-time major version of the library. + + @ingroup API +*/ +#define HID_API_VERSION_MAJOR 0 +/** @brief Static/compile-time minor version of the library. + + @ingroup API +*/ +#define HID_API_VERSION_MINOR 16 +/** @brief Static/compile-time patch version of the library. + + @ingroup API +*/ +#define HID_API_VERSION_PATCH 0 + +/* Helper macros */ +#define HID_API_AS_STR_IMPL(x) #x +#define HID_API_AS_STR(x) HID_API_AS_STR_IMPL(x) +#define HID_API_TO_VERSION_STR(v1, v2, v3) HID_API_AS_STR(v1.v2.v3) + +/** @brief Coverts a version as Major/Minor/Patch into a number: + <8 bit major><16 bit minor><8 bit patch>. + + This macro was added in version 0.12.0. + + Convenient function to be used for compile-time checks, like: + @code{.c} + #if HID_API_VERSION >= HID_API_MAKE_VERSION(0, 12, 0) + @endcode + + @ingroup API +*/ +#define HID_API_MAKE_VERSION(mj, mn, p) (((mj) << 24) | ((mn) << 8) | (p)) + +/** @brief Static/compile-time version of the library. + + This macro was added in version 0.12.0. + + @see @ref HID_API_MAKE_VERSION. + + @ingroup API +*/ +#define HID_API_VERSION HID_API_MAKE_VERSION(HID_API_VERSION_MAJOR, HID_API_VERSION_MINOR, HID_API_VERSION_PATCH) + +/** @brief Static/compile-time string version of the library. + + @ingroup API +*/ +#define HID_API_VERSION_STR HID_API_TO_VERSION_STR(HID_API_VERSION_MAJOR, HID_API_VERSION_MINOR, HID_API_VERSION_PATCH) + +/** @brief Maximum expected HID Report descriptor size in bytes. + + Since version 0.13.0, @ref HID_API_VERSION >= HID_API_MAKE_VERSION(0, 13, 0) + + @ingroup API +*/ +#define HID_API_MAX_REPORT_DESCRIPTOR_SIZE 4096 + +#ifdef __cplusplus +extern "C" { +#endif + /** A structure to hold the version numbers. */ + struct hid_api_version { + int major; /**< major version number */ + int minor; /**< minor version number */ + int patch; /**< patch version number */ + }; + + struct hid_device_; + typedef struct hid_device_ hid_device; /**< opaque hidapi structure */ + + /** @brief HID underlying bus types. + + @ingroup API + */ + typedef enum { + /** Unknown bus type */ + HID_API_BUS_UNKNOWN = 0x00, + + /** USB bus + Specifications: + https://usb.org/hid */ + HID_API_BUS_USB = 0x01, + + /** Bluetooth or Bluetooth LE bus + Specifications: + https://www.bluetooth.com/specifications/specs/human-interface-device-profile-1-1-1/ + https://www.bluetooth.com/specifications/specs/hid-service-1-0/ + https://www.bluetooth.com/specifications/specs/hid-over-gatt-profile-1-0/ */ + HID_API_BUS_BLUETOOTH = 0x02, + + /** I2C bus + Specifications: + https://docs.microsoft.com/previous-versions/windows/hardware/design/dn642101(v=vs.85) */ + HID_API_BUS_I2C = 0x03, + + /** SPI bus + Specifications: + https://www.microsoft.com/download/details.aspx?id=103325 */ + HID_API_BUS_SPI = 0x04, + + /** Virtual device + E.g.: https://elixir.bootlin.com/linux/v4.0/source/include/uapi/linux/input.h#L955 + + Since version 0.16.0, @ref HID_API_VERSION >= HID_API_MAKE_VERSION(0, 16, 0) + */ + HID_API_BUS_VIRTUAL = 0x05, + } hid_bus_type; + + /** hidapi info structure */ + struct hid_device_info { + /** Platform-specific device path */ + char *path; + /** Device Vendor ID */ + unsigned short vendor_id; + /** Device Product ID */ + unsigned short product_id; + /** Serial Number */ + wchar_t *serial_number; + /** Device Release Number in binary-coded decimal, + also known as Device Version Number */ + unsigned short release_number; + /** Manufacturer String */ + wchar_t *manufacturer_string; + /** Product string */ + wchar_t *product_string; + /** Usage Page for this Device/Interface + (Windows/Mac/hidraw only) */ + unsigned short usage_page; + /** Usage for this Device/Interface + (Windows/Mac/hidraw only) */ + unsigned short usage; + /** The USB interface which this logical device + represents. + + Valid only if the device is a USB HID device. + Set to -1 in all other cases. + */ + int interface_number; + + /** Pointer to the next device */ + struct hid_device_info *next; + + /** Underlying bus type + Since version 0.13.0, @ref HID_API_VERSION >= HID_API_MAKE_VERSION(0, 13, 0) + */ + hid_bus_type bus_type; + }; + + + /** @brief Initialize the HIDAPI library. + + This function initializes the HIDAPI library. Calling it is not + strictly necessary, as it will be called automatically by + hid_enumerate() and any of the hid_open_*() functions if it is + needed. This function should be called at the beginning of + execution however, if there is a chance of HIDAPI handles + being opened by different threads simultaneously. + + @ingroup API + + @returns + This function returns 0 on success and -1 on error. + Call hid_error(NULL) to get the failure reason. + */ + int HID_API_EXPORT HID_API_CALL hid_init(void); + + /** @brief Finalize the HIDAPI library. + + This function frees all of the static data associated with + HIDAPI. It should be called at the end of execution to avoid + memory leaks. + + @ingroup API + + @returns + This function returns 0 on success and -1 on error. + */ + int HID_API_EXPORT HID_API_CALL hid_exit(void); + + /** @brief Enumerate the HID Devices. + + This function returns a linked list of all the HID devices + attached to the system which match vendor_id and product_id. + If @p vendor_id is set to 0 then any vendor matches. + If @p product_id is set to 0 then any product matches. + If @p vendor_id and @p product_id are both set to 0, then + all HID devices will be returned. + + @ingroup API + @param vendor_id The Vendor ID (VID) of the types of device + to open. + @param product_id The Product ID (PID) of the types of + device to open. + + @returns + This function returns a pointer to a linked list of type + struct #hid_device_info, containing information about the HID devices + attached to the system, + or NULL in the case of failure or if no HID devices present in the system. + Call hid_error(NULL) to get the failure reason. + + @note The returned value by this function must to be freed by calling hid_free_enumeration(), + when not needed anymore. + */ + struct hid_device_info HID_API_EXPORT * HID_API_CALL hid_enumerate(unsigned short vendor_id, unsigned short product_id); + + /** @brief Free an enumeration Linked List + + This function frees a linked list created by hid_enumerate(). + + @ingroup API + @param devs Pointer to a list of struct_device returned from + hid_enumerate(). + */ + void HID_API_EXPORT HID_API_CALL hid_free_enumeration(struct hid_device_info *devs); + + /** @brief Callback handle. + + Callbacks handles are generated by hid_hotplug_register_callback() + and can be used to deregister callbacks. Callback handles are unique + and it is safe to call hid_hotplug_deregister_callback() on + an already deregistered callback. + + @ingroup API + */ + typedef int hid_hotplug_callback_handle; + + /** + Hotplug events + + @ingroup API + */ + typedef enum { + /** A device has been plugged in and is ready to use */ + HID_API_HOTPLUG_EVENT_DEVICE_ARRIVED = (1 << 0), + + /** A device has left and is no longer available. + It is the user's responsibility to call hid_close with a disconnected device. + */ + HID_API_HOTPLUG_EVENT_DEVICE_LEFT = (1 << 1) + } hid_hotplug_event; + + /** + Hotplug flags + + @ingroup API + */ + typedef enum { + /** Arm the callback and fire it for all matching currently attached devices. */ + HID_API_HOTPLUG_ENUMERATE = (1 << 0) + } hid_hotplug_flag; + + /** @brief Hotplug callback function type. When requesting hotplug event notifications, + you pass a pointer to a callback function of this type. + + This callback may be called by an internal event thread and as such it is + recommended the callback do minimal processing before returning. + + hidapi will call this function later, when a matching event had happened on + a matching device. + + Note that when callbacks are called from hid_hotplug_register_callback() + because of the \ref HID_API_HOTPLUG_ENUMERATE flag, the callback return + value is ignored. In other words, you cannot cause a callback to be + deregistered by returning 1 when it is called from hid_hotplug_register_callback(). + + @ingroup API + + @param callback_handle The hid_hotplug_callback_handle callback handle. + @param device The hid_device_info of device this event occurred on event that occurred. + @param event Event that occurred. + @param user_data User data provided when this callback was registered. + (Optionally NULL). + + @returns bool + Whether this callback is finished processing events. + Returning non-zero value will cause this callback to be deregistered. + */ + typedef int (HID_API_CALL *hid_hotplug_callback_fn)( + hid_hotplug_callback_handle callback_handle, + struct hid_device_info *device, + hid_hotplug_event event, + void *user_data); + + /** @brief Register a HID hotplug callback function. + + If @p vendor_id is set to 0 then any vendor matches. + If @p product_id is set to 0 then any product matches. + If @p vendor_id and @p product_id are both set to 0, then all HID devices will be notified. + + @ingroup API + + @param vendor_id The Vendor ID (VID) of the types of device to notify about. + @param product_id The Product ID (PID) of the types of device to notify about. + @param events Bitwise or of hotplug events that will trigger this callback. + See \ref hid_hotplug_event. + @param flags Bitwise or of hotplug flags that affect registration. + See \ref hid_hotplug_flag. + @param callback The callback function that will be called on device connection/disconnection. + See \ref hid_hotplug_callback_fn. + @param user_data The user data you wanted to provide to your callback function. + @param callback_handle Pointer to store the handle of the allocated callback + (Optionally NULL). + + @returns + This function returns 0 on success or -1 on error. + */ + int HID_API_EXPORT HID_API_CALL hid_hotplug_register_callback(unsigned short vendor_id, unsigned short product_id, int events, int flags, hid_hotplug_callback_fn callback, void *user_data, hid_hotplug_callback_handle *callback_handle); + + /** @brief Deregister a callback from a HID hotplug. + + This function is safe to call from within a hotplug callback. + + @ingroup API + + @param callback_handle The handle of the callback to deregister. + + @returns + This function returns 0 on success or -1 on error. + */ + int HID_API_EXPORT HID_API_CALL hid_hotplug_deregister_callback(hid_hotplug_callback_handle callback_handle); + + /** @brief Open a HID device using a Vendor ID (VID), Product ID + (PID) and optionally a serial number. + + If @p serial_number is NULL, the first device with the + specified VID and PID is opened. + + @ingroup API + @param vendor_id The Vendor ID (VID) of the device to open. + @param product_id The Product ID (PID) of the device to open. + @param serial_number The Serial Number of the device to open + (Optionally NULL). + + @returns + This function returns a pointer to a #hid_device object on + success or NULL on failure. + Call hid_error(NULL) to get the failure reason. + + @note The returned object must be freed by calling hid_close(), + when not needed anymore. + */ + HID_API_EXPORT hid_device * HID_API_CALL hid_open(unsigned short vendor_id, unsigned short product_id, const wchar_t *serial_number); + + /** @brief Open a HID device by its path name. + + The path name be determined by calling hid_enumerate(), or a + platform-specific path name can be used (eg: /dev/hidraw0 on + Linux). + + @ingroup API + @param path The path name of the device to open + + @returns + This function returns a pointer to a #hid_device object on + success or NULL on failure. + Call hid_error(NULL) to get the failure reason. + + @note The returned object must be freed by calling hid_close(), + when not needed anymore. + */ + HID_API_EXPORT hid_device * HID_API_CALL hid_open_path(const char *path); + + /** @brief Write an Output report to a HID device. + + The first byte of @p data[] must contain the Report ID. For + devices which only support a single report, this must be set + to 0x0. The remaining bytes contain the report data. Since + the Report ID is mandatory, calls to hid_write() will always + contain one more byte than the report contains. For example, + if a hid report is 16 bytes long, 17 bytes must be passed to + hid_write(), the Report ID (or 0x0, for devices with a + single report), followed by the report data (16 bytes). In + this example, the length passed in would be 17. + + hid_write() will send the data on the first interrupt OUT + endpoint, if one exists. If it does not the behaviour is as + @ref hid_send_output_report + + @ingroup API + @param dev A device handle returned from hid_open(). + @param data The data to send, including the report number as + the first byte. + @param length The length in bytes of the data to send. + + @returns + This function returns the actual number of bytes written and + -1 on error. + Call hid_error(dev) to get the failure reason. + */ + int HID_API_EXPORT HID_API_CALL hid_write(hid_device *dev, const unsigned char *data, size_t length); + + /** @brief Read an Input report from a HID device with timeout. + + Input reports are returned + to the host through the INTERRUPT IN endpoint. The first byte will + contain the Report number if the device uses numbered reports. + + @ingroup API + @param dev A device handle returned from hid_open(). + @param data A buffer to put the read data into. + @param length The number of bytes to read. For devices with + multiple reports, make sure to read an extra byte for + the report number. + @param milliseconds timeout in milliseconds or -1 for blocking wait. + + @returns + This function returns the actual number of bytes read and + -1 on error. + Call hid_read_error(dev) to get the failure reason. + If no packet was available to be read within + the timeout period, this function returns 0. + + @note This function doesn't change the buffer returned by the hid_error(dev). + */ + int HID_API_EXPORT HID_API_CALL hid_read_timeout(hid_device *dev, unsigned char *data, size_t length, int milliseconds); + + /** @brief Read an Input report from a HID device. + + Input reports are returned + to the host through the INTERRUPT IN endpoint. The first byte will + contain the Report number if the device uses numbered reports. + + @ingroup API + @param dev A device handle returned from hid_open(). + @param data A buffer to put the read data into. + @param length The number of bytes to read. For devices with + multiple reports, make sure to read an extra byte for + the report number. + + @returns + This function returns the actual number of bytes read and + -1 on error. + Call hid_read_error(dev) to get the failure reason. + If no packet was available to be read and + the handle is in non-blocking mode, this function returns 0. + + @note This function doesn't change the buffer returned by the hid_error(dev). + */ + int HID_API_EXPORT HID_API_CALL hid_read(hid_device *dev, unsigned char *data, size_t length); + + /** @brief Get a string describing the last error which occurred during hid_read/hid_read_timeout. + + Since version 0.15.0, @ref HID_API_VERSION >= HID_API_MAKE_VERSION(0, 15, 0) + + This function is intended for logging/debugging purposes. + + This function guarantees to never return NULL for a valid @ref dev. + If there was no error in the last call to hid_read/hid_read_error - + the returned string clearly indicates that. + + Strings returned from hid_read_error() must not be freed by the user, + i.e. owned by HIDAPI library. + Device-specific error string may remain allocated at most until hid_close() is called. + + @ingroup API + @param dev A device handle. Shall never be NULL. + + @returns + A string describing the hid_read/hid_read_timeout error (if any). + */ + HID_API_EXPORT const wchar_t* HID_API_CALL hid_read_error(hid_device *dev); + + /** @brief Set the device handle to be non-blocking. + + In non-blocking mode calls to hid_read() will return + immediately with a value of 0 if there is no data to be + read. In blocking mode, hid_read() will wait (block) until + there is data to read before returning. + + Nonblocking can be turned on and off at any time. + + @ingroup API + @param dev A device handle returned from hid_open(). + @param nonblock enable or not the nonblocking reads + - 1 to enable nonblocking + - 0 to disable nonblocking. + + @returns + This function returns 0 on success and -1 on error. + Call hid_error(dev) to get the failure reason. + */ + int HID_API_EXPORT HID_API_CALL hid_set_nonblocking(hid_device *dev, int nonblock); + + /** @brief Send a Feature report to the device. + + Feature reports are sent over the Control endpoint as a + Set_Report transfer. The first byte of @p data[] must + contain the Report ID. For devices which only support a + single report, this must be set to 0x0. The remaining bytes + contain the report data. Since the Report ID is mandatory, + calls to hid_send_feature_report() will always contain one + more byte than the report contains. For example, if a hid + report is 16 bytes long, 17 bytes must be passed to + hid_send_feature_report(): the Report ID (or 0x0, for + devices which do not use numbered reports), followed by the + report data (16 bytes). In this example, the length passed + in would be 17. + + @ingroup API + @param dev A device handle returned from hid_open(). + @param data The data to send, including the report number as + the first byte. + @param length The length in bytes of the data to send, including + the report number. + + @returns + This function returns the actual number of bytes written and + -1 on error. + Call hid_error(dev) to get the failure reason. + */ + int HID_API_EXPORT HID_API_CALL hid_send_feature_report(hid_device *dev, const unsigned char *data, size_t length); + + /** @brief Get a feature report from a HID device. + + Set the first byte of @p data[] to the Report ID of the + report to be read. Make sure to allow space for this + extra byte in @p data[]. Upon return, the first byte will + still contain the Report ID, and the report data will + start in data[1]. + + @ingroup API + @param dev A device handle returned from hid_open(). + @param data A buffer to put the read data into, including + the Report ID. Set the first byte of @p data[] to the + Report ID of the report to be read, or set it to zero + if your device does not use numbered reports. + @param length The number of bytes to read, including an + extra byte for the report ID. The buffer can be longer + than the actual report. + + @returns + This function returns the number of bytes read plus + one for the report ID (which is still in the first + byte), or -1 on error. + Call hid_error(dev) to get the failure reason. + */ + int HID_API_EXPORT HID_API_CALL hid_get_feature_report(hid_device *dev, unsigned char *data, size_t length); + + /** @brief Send a Output report to the device. + + Since version 0.15.0, @ref HID_API_VERSION >= HID_API_MAKE_VERSION(0, 15, 0) + + Output reports are sent over the Control endpoint as a + Set_Report transfer. The first byte of @p data[] must + contain the Report ID. For devices which only support a + single report, this must be set to 0x0. The remaining bytes + contain the report data. Since the Report ID is mandatory, + calls to hid_send_output_report() will always contain one + more byte than the report contains. For example, if a hid + report is 16 bytes long, 17 bytes must be passed to + hid_send_output_report(): the Report ID (or 0x0, for + devices which do not use numbered reports), followed by the + report data (16 bytes). In this example, the length passed + in would be 17. + + This function sets the return value of hid_error(). + + @ingroup API + @param dev A device handle returned from hid_open(). + @param data The data to send, including the report number as + the first byte. + @param length The length in bytes of the data to send, including + the report number. + + @returns + This function returns the actual number of bytes written and + -1 on error. + + @see @ref hid_write + */ + int HID_API_EXPORT HID_API_CALL hid_send_output_report(hid_device* dev, const unsigned char* data, size_t length); + + /** @brief Get a input report from a HID device. + + Since version 0.10.0, @ref HID_API_VERSION >= HID_API_MAKE_VERSION(0, 10, 0) + + Set the first byte of @p data[] to the Report ID of the + report to be read. Make sure to allow space for this + extra byte in @p data[]. Upon return, the first byte will + still contain the Report ID, and the report data will + start in data[1]. + + @ingroup API + @param dev A device handle returned from hid_open(). + @param data A buffer to put the read data into, including + the Report ID. Set the first byte of @p data[] to the + Report ID of the report to be read, or set it to zero + if your device does not use numbered reports. + @param length The number of bytes to read, including an + extra byte for the report ID. The buffer can be longer + than the actual report. + + @returns + This function returns the number of bytes read plus + one for the report ID (which is still in the first + byte), or -1 on error. + Call hid_error(dev) to get the failure reason. + */ + int HID_API_EXPORT HID_API_CALL hid_get_input_report(hid_device *dev, unsigned char *data, size_t length); + + /** @brief Close a HID device. + + @ingroup API + @param dev A device handle returned from hid_open(). + */ + void HID_API_EXPORT HID_API_CALL hid_close(hid_device *dev); + + /** @brief Get The Manufacturer String from a HID device. + + @ingroup API + @param dev A device handle returned from hid_open(). + @param string A wide string buffer to put the data into. + @param maxlen The length of the buffer in multiples of wchar_t. + + @returns + This function returns 0 on success and -1 on error. + Call hid_error(dev) to get the failure reason. + */ + int HID_API_EXPORT_CALL hid_get_manufacturer_string(hid_device *dev, wchar_t *string, size_t maxlen); + + /** @brief Get The Product String from a HID device. + + @ingroup API + @param dev A device handle returned from hid_open(). + @param string A wide string buffer to put the data into. + @param maxlen The length of the buffer in multiples of wchar_t. + + @returns + This function returns 0 on success and -1 on error. + Call hid_error(dev) to get the failure reason. + */ + int HID_API_EXPORT_CALL hid_get_product_string(hid_device *dev, wchar_t *string, size_t maxlen); + + /** @brief Get The Serial Number String from a HID device. + + @ingroup API + @param dev A device handle returned from hid_open(). + @param string A wide string buffer to put the data into. + @param maxlen The length of the buffer in multiples of wchar_t. + + @returns + This function returns 0 on success and -1 on error. + Call hid_error(dev) to get the failure reason. + */ + int HID_API_EXPORT_CALL hid_get_serial_number_string(hid_device *dev, wchar_t *string, size_t maxlen); + + /** @brief Get The struct #hid_device_info from a HID device. + + Since version 0.13.0, @ref HID_API_VERSION >= HID_API_MAKE_VERSION(0, 13, 0) + + @ingroup API + @param dev A device handle returned from hid_open(). + + @returns + This function returns a pointer to the struct #hid_device_info + for this hid_device, or NULL in the case of failure. + Call hid_error(dev) to get the failure reason. + This struct is valid until the device is closed with hid_close(). + + @note The returned object is owned by the @p dev, and SHOULD NOT be freed by the user. + */ + struct hid_device_info HID_API_EXPORT * HID_API_CALL hid_get_device_info(hid_device *dev); + + /** @brief Get a string from a HID device, based on its string index. + + @ingroup API + @param dev A device handle returned from hid_open(). + @param string_index The index of the string to get. + @param string A wide string buffer to put the data into. + @param maxlen The length of the buffer in multiples of wchar_t. + + @returns + This function returns 0 on success and -1 on error. + Call hid_error(dev) to get the failure reason. + */ + int HID_API_EXPORT_CALL hid_get_indexed_string(hid_device *dev, int string_index, wchar_t *string, size_t maxlen); + + /** @brief Get a report descriptor from a HID device. + + Since version 0.14.0, @ref HID_API_VERSION >= HID_API_MAKE_VERSION(0, 14, 0) + + User has to provide a preallocated buffer where descriptor will be copied to. + The recommended size for preallocated buffer is @ref HID_API_MAX_REPORT_DESCRIPTOR_SIZE bytes. + + @ingroup API + @param dev A device handle returned from hid_open(). + @param buf The buffer to copy descriptor into. + @param buf_size The size of the buffer in bytes. + + @returns + This function returns non-negative number of bytes actually copied, or -1 on error. + */ + int HID_API_EXPORT_CALL hid_get_report_descriptor(hid_device *dev, unsigned char *buf, size_t buf_size); + + /** @brief Get a string describing the last error which occurred. + + This function is intended for logging/debugging purposes. + + This function guarantees to never return NULL. + If there was no error in the last function call - + the returned string clearly indicates that. + + Any HIDAPI function that can explicitly indicate an execution failure + (e.g. by an error code, or by returning NULL) - may set the error string, + to be returned by this function. + + Strings returned from hid_error() must not be freed by the user, + i.e. owned by HIDAPI library. + Device-specific error string may remain allocated at most until hid_close() is called. + Global error string may remain allocated at most until hid_exit() is called. + + @ingroup API + @param dev A device handle returned from hid_open(), + or NULL to get the last non-device-specific error + (e.g. for errors in hid_open() or hid_enumerate()). + + @returns + A string describing the last error (if any). + */ + HID_API_EXPORT const wchar_t* HID_API_CALL hid_error(hid_device *dev); + + /** @brief Get a runtime version of the library. + + This function is thread-safe. + + @ingroup API + + @returns + Pointer to statically allocated struct, that contains version. + */ + HID_API_EXPORT const struct hid_api_version* HID_API_CALL hid_version(void); + + + /** @brief Get a runtime version string of the library. + + This function is thread-safe. + + @ingroup API + + @returns + Pointer to statically allocated string, that contains version string. + */ + HID_API_EXPORT const char* HID_API_CALL hid_version_str(void); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/dependencies/hidapi-hotplug-win/include/hidapi_winapi.h b/dependencies/hidapi-hotplug-win/include/hidapi_winapi.h new file mode 100644 index 000000000..7ab5e9728 --- /dev/null +++ b/dependencies/hidapi-hotplug-win/include/hidapi_winapi.h @@ -0,0 +1,94 @@ +/******************************************************* + HIDAPI - Multi-Platform library for + communication with HID devices. + + libusb/hidapi Team + + Copyright 2022, All Rights Reserved. + + At the discretion of the user of this library, + this software may be licensed under the terms of the + GNU General Public License v3, a BSD-Style license, or the + original HIDAPI license as outlined in the LICENSE.txt, + LICENSE-gpl3.txt, LICENSE-bsd.txt, and LICENSE-orig.txt + files located at the root of the source distribution. + These files may also be found in the public source + code repository located at: + https://github.com/libusb/hidapi . +********************************************************/ + +/** @file + * @defgroup API hidapi API + * + * Since version 0.12.0, @ref HID_API_VERSION >= HID_API_MAKE_VERSION(0, 12, 0) + */ + +#ifndef HIDAPI_WINAPI_H__ +#define HIDAPI_WINAPI_H__ + +#include + +#include + +#include "hidapi.h" + +#ifdef __cplusplus +extern "C" { +#endif + + /** @brief Get the container ID for a HID device. + + Since version 0.12.0, @ref HID_API_VERSION >= HID_API_MAKE_VERSION(0, 12, 0) + + This function returns the `DEVPKEY_Device_ContainerId` property of + the given device. This can be used to correlate different + interfaces/ports on the same hardware device. + + @ingroup API + @param dev A device handle returned from hid_open(). + @param container_id The device's container ID on return. + + @returns + This function returns 0 on success and -1 on error. + */ + int HID_API_EXPORT_CALL hid_winapi_get_container_id(hid_device *dev, GUID *container_id); + + /** + * @brief Reconstructs a HID Report Descriptor from a Win32 HIDP_PREPARSED_DATA structure. + * This reconstructed report descriptor is logical identical to the real report descriptor, + * but not byte wise identical. + * + * @param[in] hidp_preparsed_data Pointer to the HIDP_PREPARSED_DATA to read, i.e.: the value of PHIDP_PREPARSED_DATA, + * as returned by HidD_GetPreparsedData WinAPI function. + * @param buf Pointer to the buffer where the report descriptor should be stored. + * @param[in] buf_size Size of the buffer. The recommended size for the buffer is @ref HID_API_MAX_REPORT_DESCRIPTOR_SIZE bytes. + * + * @return Returns size of reconstructed report descriptor if successful, -1 for error. + */ + int HID_API_EXPORT_CALL hid_winapi_descriptor_reconstruct_pp_data(void *hidp_preparsed_data, unsigned char *buf, size_t buf_size); + + /** + * @brief Sets the timeout for hid_write operation. + * + * Since version 0.15.0, @ref HID_API_VERSION >= HID_API_MAKE_VERSION(0, 15, 0) + * + * The default timeout is 1sec for winapi backend. + * + * In case if 1sec is not enough, on in case of multi-platform development, + * the recommended value is 5sec, e.g. to match (unconfigurable) 5sec timeout + * set for hidraw (linux kernel) implementation. + * + * When the timeout is set to 0, hid_write function becomes non-blocking and would exit immediately. + * When the timeout is set to INFINITE ((DWORD)-1), the function will not exit, + * until the write operation is performed or an error occurred. + * See dwMilliseconds parameter documentation of WaitForSingleObject function. + * + * @param timeout New timeout value in milliseconds. + */ + void HID_API_EXPORT_CALL hid_winapi_set_write_timeout(hid_device *dev, unsigned long timeout); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/dependencies/hidapi-hotplug-win/x64/hidapi-hotplug.dll b/dependencies/hidapi-hotplug-win/x64/hidapi-hotplug.dll new file mode 100644 index 0000000000000000000000000000000000000000..51fd821cbdfe40fc0c65be5825b664fbbf8de232 GIT binary patch literal 45056 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 literal 0 HcmV?d00001 diff --git a/dependencies/hidapi-hotplug-win/x64/hidapi-hotplug.lib b/dependencies/hidapi-hotplug-win/x64/hidapi-hotplug.lib new file mode 100644 index 0000000000000000000000000000000000000000..f6d8865afc3fd81101698ea1ce7ee9fd9d7ef0ec GIT binary patch literal 8500 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%0DQ{XL5J-MB$MCwirObuXpZ)L z@YBZ|5UYrziH%1g5GC#V;KE10QTvLsh~~9R0nqwv8Hl&hf-M>8b0;1y(MXp&v28o%f*|kl_V~=kuJ3mt zGM)L&iy0be*{)pTKp{q4`6WD0y^!y@mz<2xmdFl;_^+kX8=ev{~*KrSWoslF?`0a&$$x2f;a&dt+ z)0J)?yvMKtcpzKT?>`!RLTmbZ?tyvIlYsf|a~ax~UA6ZKt%{?Wr^!N~1tt0Tn(^%& z%E#4qG8g|S^M60P(zZD94b4#upLqBIjZaeK?3e~Ol4X56Ya;pX^n1hU_1W< zl0f?&;gDo~xbyg{+q4sD6xhyt5DBo~HU}kZW50UhRseCEI4-|xDFmJ*7j3_Obdk;k z%`QCiHm3lXdn9fA@cPKJeTdn@F;{kBQ~+;6lAXwR#~1&gok*+88`_XK{y!=Nz9b(f mMrNN-K5ld2d#}AD4)~6B*;(z2uQ%MFvrK!|1M_N40{0(EjUeLy literal 0 HcmV?d00001 diff --git a/fedora/OpenRGB.spec.in b/fedora/OpenRGB.spec.in index 884eaaea8..aa6b13880 100644 --- a/fedora/OpenRGB.spec.in +++ b/fedora/OpenRGB.spec.in @@ -8,7 +8,7 @@ Summary: Open source RGB lighting control that doesn't depend on manufact License: GPLv2 URL: https://gitlab.com/CalcProgrammer1/%{_name} -BuildRequires: gcc-c++ libusbx-devel libstdc++-devel qt6-qtbase-devel qt6-linguist desktop-file-utils hidapi-devel mbedtls-devel systemd-rpm-macros +BuildRequires: gcc-c++ libusbx-devel libstdc++-devel qt6-qtbase-devel qt6-linguist desktop-file-utils hidapi-hotplug-devel mbedtls-devel systemd-rpm-macros Requires: hicolor-icon-theme %description diff --git a/hidapi_wrapper/hidapi_wrapper.h b/hidapi_wrapper/hidapi_wrapper.h index b04246e05..d7d1b2d59 100644 --- a/hidapi_wrapper/hidapi_wrapper.h +++ b/hidapi_wrapper/hidapi_wrapper.h @@ -19,31 +19,40 @@ #include #endif -/*-----------------------------------------------------*\ -| Type definitions for libhidapi function pointers | -\*-----------------------------------------------------*/ -typedef int (*hidapi_wrapper_send_feature_report) (hid_device*, const unsigned char*, size_t); -typedef int (*hidapi_wrapper_get_feature_report) (hid_device*, unsigned char*, size_t); -typedef int (*hidapi_wrapper_get_serial_number_string) (hid_device*, wchar_t*, size_t); -typedef hid_device* (*hidapi_wrapper_open_path) (const char*); -typedef hid_device_info* (*hidapi_wrapper_enumerate) (unsigned short, unsigned short); -typedef void (*hidapi_wrapper_free_enumeration) (hid_device_info*); -typedef void (*hidapi_wrapper_close) (hid_device*); -typedef const wchar_t* (*hidapi_wrapper_error) (hid_device*); +/*---------------------------------------------------------*\ +| Type definitions for libhidapi function pointers | +\*---------------------------------------------------------*/ +typedef int (*hidapi_wrapper_send_feature_report) (hid_device*, const unsigned char*, size_t); +typedef int (*hidapi_wrapper_get_feature_report) (hid_device*, unsigned char*, size_t); +typedef int (*hidapi_wrapper_get_serial_number_string) (hid_device*, wchar_t*, size_t); +typedef hid_device* (*hidapi_wrapper_open_path) (const char*); +typedef hid_device_info* (*hidapi_wrapper_enumerate) (unsigned short, unsigned short); +typedef void (*hidapi_wrapper_free_enumeration) (hid_device_info*); +typedef void (*hidapi_wrapper_close) (hid_device*); +typedef const wchar_t* (*hidapi_wrapper_error) (hid_device*); -/*-----------------------------------------------------*\ -| See comment at top of HyperXQuadcastSDetect.cpp for | -| details about the hidapi wrapper for this device | -\*-----------------------------------------------------*/ +#if(HID_HOTPLUG_ENABLED) +typedef int (*hidapi_wrapper_hotplug_register_callback) (unsigned short vendor_id, unsigned short product_id, int events, int flags, hid_hotplug_callback_fn callback, void* user_data, hid_hotplug_callback_handle* callback_handle); +typedef int (*hidapi_wrapper_hotplug_deregister_callback) (hid_hotplug_callback_handle callback_handle); +#endif + +/*---------------------------------------------------------*\ +| See comment at top of HyperXQuadcastSDetect.cpp for | +| details about the hidapi wrapper for this device | +\*---------------------------------------------------------*/ struct hidapi_wrapper { - void* dyn_handle; - hidapi_wrapper_send_feature_report hid_send_feature_report; - hidapi_wrapper_get_feature_report hid_get_feature_report; - hidapi_wrapper_get_serial_number_string hid_get_serial_number_string; - hidapi_wrapper_open_path hid_open_path; - hidapi_wrapper_enumerate hid_enumerate; - hidapi_wrapper_free_enumeration hid_free_enumeration; - hidapi_wrapper_close hid_close; - hidapi_wrapper_error hid_error; + void* dyn_handle; + hidapi_wrapper_send_feature_report hid_send_feature_report; + hidapi_wrapper_get_feature_report hid_get_feature_report; + hidapi_wrapper_get_serial_number_string hid_get_serial_number_string; + hidapi_wrapper_open_path hid_open_path; + hidapi_wrapper_enumerate hid_enumerate; + hidapi_wrapper_free_enumeration hid_free_enumeration; + hidapi_wrapper_close hid_close; + hidapi_wrapper_error hid_error; +#if(HID_HOTPLUG_ENABLED) + hidapi_wrapper_hotplug_register_callback hid_hotplug_register_callback; + hidapi_wrapper_hotplug_deregister_callback hid_hotplug_deregister_callback; +#endif }; diff --git a/qt/OpenRGBSoftwareInfoPage/OpenRGBSoftwareInfoPage.cpp b/qt/OpenRGBSoftwareInfoPage/OpenRGBSoftwareInfoPage.cpp index 1e3b7beb8..3ac0484d7 100644 --- a/qt/OpenRGBSoftwareInfoPage/OpenRGBSoftwareInfoPage.cpp +++ b/qt/OpenRGBSoftwareInfoPage/OpenRGBSoftwareInfoPage.cpp @@ -28,6 +28,11 @@ OpenRGBSoftwareInfoPage::OpenRGBSoftwareInfoPage(QWidget *parent) : ui->GitCommitDateValue->setText(GIT_COMMIT_DATE); ui->GitBranchValue->setText(GIT_BRANCH); ui->OsVersionValue->setText(QSysInfo::prettyProductName()); +#if(HID_HOTPLUG_ENABLED) + ui->HIDHotplugValue->setText("Supported"); +#else + ui->HIDHotplugValue->setText("Unsupported"); +#endif } OpenRGBSoftwareInfoPage::~OpenRGBSoftwareInfoPage() diff --git a/qt/OpenRGBSoftwareInfoPage/OpenRGBSoftwareInfoPage.ui b/qt/OpenRGBSoftwareInfoPage/OpenRGBSoftwareInfoPage.ui index 7b4950fd0..61ffd2fe8 100644 --- a/qt/OpenRGBSoftwareInfoPage/OpenRGBSoftwareInfoPage.ui +++ b/qt/OpenRGBSoftwareInfoPage/OpenRGBSoftwareInfoPage.ui @@ -35,21 +35,86 @@ QFrame::Sunken - - + + - Build Date Value + SDK Version: + + + + + + + Website: + + + Git Commit ID Value + + + Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + + + Build Date: + + + + + + + Plugin API Version Value + + + + + + + Plugin API Version: + + + + + + + Git Commit Date: + + + + + + + <a href="https://openrgb.org">https://openrgb.org</a> + + + Qt::RichText + + + true + + + + + + + Git Branch: + + + + Git Commit Date Value - + <a href="https://gitlab.com/CalcProgrammer1/OpenRGB">https://gitlab.com/CalcProgrammer1/OpenRGB</a> @@ -62,14 +127,21 @@ - - + + - SDK Version: + SDK Version Value - + + + + Git Commit ID: + + + + Qt::Vertical @@ -83,105 +155,12 @@ - - - Website: - - - - - - - Build Date: - - - - - - - Git Commit ID: - - - - - - - Git Commit Date: - - - - - - - Qt Version Value - - - - GitLab: - - - - Git Branch Value - - - - - - - <a href="https://openrgb.org">https://openrgb.org</a> - - - Qt::RichText - - - true - - - - - - - Plugin API Version Value - - - - - - - Git Commit ID Value - - - Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse - - - - - - - Plugin API Version: - - - - - - - Git Branch: - - - - - - - SDK Version Value - - - @@ -189,6 +168,27 @@ + + + + HID Hotplug: + + + + + + + Git Branch Value + + + + + + + Qt Version Value + + + @@ -203,6 +203,20 @@ + + + + Build Date Value + + + + + + + HID Hotplug Value + + + diff --git a/scripts/build-appimage.sh b/scripts/build-appimage.sh index 211e78225..55f837164 100755 --- a/scripts/build-appimage.sh +++ b/scripts/build-appimage.sh @@ -98,7 +98,16 @@ make install INSTALL_ROOT=AppDir #-----------------------------------------------------------------------# export QML_SOURCES_PATHS="$REPO_ROOT"/src -linuxdeploy-"$ARCH_LINUXDEPLOY".AppImage --appdir AppDir -e "$TARGET" -i "$REPO_ROOT"/qt/org.openrgb.OpenRGB.png -d "$REPO_ROOT"/qt/org.openrgb.OpenRGB.desktop +#-----------------------------------------------------------------------# +# We need to bundle the libhidapi-libusb library for wrapped HID # +# devices, find its path using ldconfig # +#-----------------------------------------------------------------------# +LIBHIDAPI_LIBUSB_PATH=$(find /usr/lib -name "libhidapi-hotplug-libusb.so") +if [ ! -f $LIBHIDAPI_LIBUSB_PATH ]; then + LIBHIDAPI_LIBUSB_PATH=$(find /usr/lib -name "libhidapi-libusb.so") +fi + +linuxdeploy-"$ARCH_LINUXDEPLOY".AppImage --appdir AppDir -e "$TARGET" -i "$REPO_ROOT"/qt/org.openrgb.OpenRGB.png -d "$REPO_ROOT"/qt/org.openrgb.OpenRGB.desktop --library "$LIBHIDAPI_LIBUSB_PATH" linuxdeploy-plugin-qt-"$ARCH_LINUXDEPLOY".AppImage --appdir AppDir linuxdeploy-"$ARCH_LINUXDEPLOY".AppImage --appdir AppDir --output appimage