rawinput: fix hotplug for keyboard and mouse (#810)

## Link to GitHub Issue or related Pull Request, if one exists
n/a

## Description of change
Fix a long-standing issue of hotplug not working for keyboards and mice.
The code only registered for HID notifications; we also need to do it
for the keyboard GUID and mouse GUID.

## Testing
Confirmed that keyboards and mice can be hot unplugged and plugged.
This commit is contained in:
bicarus
2026-07-18 17:38:59 -07:00
committed by GitHub
parent 61a0220c1a
commit 2dea0d27bf
3 changed files with 45 additions and 19 deletions
+37 -14
View File
@@ -4,6 +4,10 @@
#include <dbt.h> #include <dbt.h>
#include <devguid.h> #include <devguid.h>
#include <hidclass.h>
#include <ntddkbd.h>
#include <ntddmou.h>
#include <objbase.h>
#include "misc/eamuse.h" #include "misc/eamuse.h"
#include "rawinput.h" #include "rawinput.h"
@@ -12,31 +16,42 @@
namespace rawinput { namespace rawinput {
DEFINE_GUID(GUID_HID, 0x4D1E55B2L, 0xF16F, 0x11CF, 0x88, 0xCB, 0x00, 0x11, 0x11, 0x00, 0x00, 0x30); static bool is_rawinput_interface(const GUID &guid) {
return IsEqualGUID(guid, GUID_DEVINTERFACE_HID)
|| IsEqualGUID(guid, GUID_DEVINTERFACE_KEYBOARD)
|| IsEqualGUID(guid, GUID_DEVINTERFACE_MOUSE);
}
HotplugManager::HotplugManager(RawInputManager *ri_mgr, HWND hWnd) : ri_mgr(ri_mgr) { HotplugManager::HotplugManager(RawInputManager *ri_mgr, HWND hWnd) : ri_mgr(ri_mgr) {
auto register_notification = [hWnd](const GUID &guid, const char *name) -> HANDLE {
// init settings DEV_BROADCAST_DEVICEINTERFACE settings {
DEV_BROADCAST_DEVICEINTERFACE settings_hid {
.dbcc_size = sizeof(DEV_BROADCAST_DEVICEINTERFACE), .dbcc_size = sizeof(DEV_BROADCAST_DEVICEINTERFACE),
.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE, .dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE,
.dbcc_reserved = 0, .dbcc_reserved = 0,
.dbcc_classguid = GUID_HID, .dbcc_classguid = guid,
.dbcc_name = {0}, .dbcc_name = {0},
}; };
// register notifications auto notification = RegisterDeviceNotification(hWnd, &settings, DEVICE_NOTIFY_WINDOW_HANDLE);
this->hotplug_hid = RegisterDeviceNotification(hWnd, &settings_hid, DEVICE_NOTIFY_WINDOW_HANDLE); if (notification == nullptr) {
if (this->hotplug_hid == nullptr) { log_warning("hotplug", "failed to register {} notifications: {}", name, GetLastError());
log_warning("hotplug", "failed to register HID notifications: {}", GetLastError());
} }
return notification;
};
// register notifications
this->hotplug_hid = register_notification(GUID_DEVINTERFACE_HID, "HID");
this->hotplug_keyboard = register_notification(GUID_DEVINTERFACE_KEYBOARD, "keyboard");
this->hotplug_mouse = register_notification(GUID_DEVINTERFACE_MOUSE, "mouse");
} }
HotplugManager::~HotplugManager() { HotplugManager::~HotplugManager() {
// unregister notifications // unregister notifications
if (this->hotplug_hid != nullptr) { for (auto notification : {this->hotplug_hid, this->hotplug_keyboard, this->hotplug_mouse}) {
UnregisterDeviceNotification(this->hotplug_hid); if (notification != nullptr) {
UnregisterDeviceNotification(notification);
}
} }
} }
@@ -54,11 +69,16 @@ namespace rawinput {
switch (hdr->dbch_devicetype) { switch (hdr->dbch_devicetype) {
case DBT_DEVTYP_DEVICEINTERFACE: { case DBT_DEVTYP_DEVICEINTERFACE: {
auto dev = (const DEV_BROADCAST_DEVICEINTERFACE *) hdr; auto dev = (const DEV_BROADCAST_DEVICEINTERFACE *) hdr;
this->ri_mgr->midi_scan_start();
// check if class is not HID // check if this is one of the registered RawInput interfaces
if (memcmp(&dev->dbcc_classguid, &GUID_HID, sizeof(GUID_HID)) != 0) if (!is_rawinput_interface(dev->dbcc_classguid)) {
break; break;
}
// keyboard and mouse class events accompany the HID event, so scan MIDI only once
if (IsEqualGUID(dev->dbcc_classguid, GUID_DEVINTERFACE_HID)) {
this->ri_mgr->midi_scan_start();
}
// hotplug // hotplug
std::string name(dev->dbcc_name); std::string name(dev->dbcc_name);
@@ -109,6 +129,9 @@ namespace rawinput {
switch (hdr->dbch_devicetype) { switch (hdr->dbch_devicetype) {
case DBT_DEVTYP_DEVICEINTERFACE: { case DBT_DEVTYP_DEVICEINTERFACE: {
auto dev = (DEV_BROADCAST_DEVICEINTERFACE*) hdr; auto dev = (DEV_BROADCAST_DEVICEINTERFACE*) hdr;
if (!is_rawinput_interface(dev->dbcc_classguid)) {
break;
}
std::string name(dev->dbcc_name); std::string name(dev->dbcc_name);
// destruct device // destruct device
+3 -1
View File
@@ -9,7 +9,9 @@ namespace rawinput {
class HotplugManager { class HotplugManager {
private: private:
RawInputManager *ri_mgr; RawInputManager *ri_mgr;
HANDLE hotplug_hid; HANDLE hotplug_hid = nullptr;
HANDLE hotplug_keyboard = nullptr;
HANDLE hotplug_mouse = nullptr;
public: public:
HotplugManager(RawInputManager *ri_mgr, HWND hwnd); HotplugManager(RawInputManager *ri_mgr, HWND hwnd);
+2 -1
View File
@@ -246,7 +246,8 @@ void rawinput::RawInputManager::devices_scan_rawinput(const std::string &device_
return; return;
} }
// iterate devices // hotplug gives us an interface path rather than a RawInput handle, so find its
// matching entry; an empty path means this is a full device scan
for (UINT device_cur_index = 0; device_cur_index < device_no; device_cur_index++) { for (UINT device_cur_index = 0; device_cur_index < device_no; device_cur_index++) {
auto device = &device_list.get()[device_cur_index]; auto device = &device_list.get()[device_cur_index];
if (device_name.length() == 0) { if (device_name.length() == 0) {