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 229c2747a..e3de1a593 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/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 3afda6f10..adeb486fa 100644 --- a/Controllers/JGINYUEInternalUSBV2Controller/JGINYUEInternalUSBV2Controller.cpp +++ b/Controllers/JGINYUEInternalUSBV2Controller/JGINYUEInternalUSBV2Controller.cpp @@ -66,7 +66,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..941bf2918 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,10 @@ 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++) { +#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 +1418,10 @@ 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++) { +#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 +1438,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 +1669,112 @@ 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]", DETECTIONMANAGER, device->vendor_id, device->product_id); + + 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]", DETECTIONMANAGER, device->vendor_id, device->product_id); + + std::string location = "HID: "; + location.append(device->path); + + // User data is the pointer to the controller being removed + RGBController* controller = (RGBController*)(user_data); + + LOG_DEBUG("Checking device location:\"%s\":\"%s\"", location.c_str(), controller->location.c_str()); + + if(controller && controller->location == location) + { + 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]", DETECTIONMANAGER, device->vendor_id, device->product_id); + + 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 b4792725d..c989ad2e7 100644 --- a/OpenRGB.pro +++ b/OpenRGB.pro @@ -328,7 +328,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 +374,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 +387,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 +401,7 @@ win32:DEFINES += _CRT_SECURE_NO_WARNINGS \ _WINSOCK_DEPRECATED_NO_WARNINGS \ WIN32_LEAN_AND_MEAN \ + HID_HOTPLUG_ENABLED=1 \ win32:RC_ICONS += \ qt/OpenRGB.ico @@ -432,7 +433,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 +447,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 +502,28 @@ 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 } 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 + } } } @@ -710,10 +717,11 @@ macx { PKGCONFIG += \ libusb-1.0 \ - hidapi + hidapi-hotplug DEFINES += \ USE_HID_USAGE \ + HID_HOTPLUG_ENABLED=1 \ QMAKE_CXXFLAGS += \ -Wno-narrowing \ 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..089d330ac --- /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..d032ea8fe --- /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 000000000..6112781e6 Binary files /dev/null and b/dependencies/hidapi-hotplug-win/x64/hidapi-hotplug.dll differ 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 000000000..f6d8865af Binary files /dev/null and b/dependencies/hidapi-hotplug-win/x64/hidapi-hotplug.lib differ diff --git a/dependencies/hidapi-hotplug-win/x86/hidapi-hotplug.dll b/dependencies/hidapi-hotplug-win/x86/hidapi-hotplug.dll new file mode 100644 index 000000000..5b9324c97 Binary files /dev/null and b/dependencies/hidapi-hotplug-win/x86/hidapi-hotplug.dll differ diff --git a/dependencies/hidapi-hotplug-win/x86/hidapi-hotplug.lib b/dependencies/hidapi-hotplug-win/x86/hidapi-hotplug.lib new file mode 100644 index 000000000..082017d1e Binary files /dev/null and b/dependencies/hidapi-hotplug-win/x86/hidapi-hotplug.lib differ 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/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