This seems to be relied on by some versions of [this Unreal Engine input plugin](https://www.unrealengine.com/marketplace/en-US/product/wm-input-manager)
Note: I'm not sure how to deal with `HID_USAGE_GENERIC_KEYPAD`, which (I think) would fall under `RIM_TYPEKEYBOARD`. Do we need to store extra info to differentiate these from `HID_USAGE_GENERIC_KEYBOARD` or is there something in the device info struct that can differentiate them?
-- v4: user32: Post WM_INPUT_DEVICE_CHANGE when registering for notifications user32: Add tests for WM_INPUT_DEVICE_CHANGE messages
From: Evan Tang etang@codeweavers.com
When registering for WM_INPUT_DEVICE_CHANGE messages, one should be sent for every existing matching device. --- dlls/user32/tests/input.c | 49 +++++++++++++++++++++++++++++++++++---- 1 file changed, 44 insertions(+), 5 deletions(-)
diff --git a/dlls/user32/tests/input.c b/dlls/user32/tests/input.c index f8b40099091..23db1aceafd 100644 --- a/dlls/user32/tests/input.c +++ b/dlls/user32/tests/input.c @@ -2108,7 +2108,9 @@ static void test_RegisterRawInputDevices(void) DestroyWindow(hwnd); }
-static int rawinputbuffer_wndproc_count; +static int rawinputbuffer_wndproc_wm_input_count; +static int rawinputbuffer_wndproc_wm_input_device_change_count; +static int rawinputbuffer_wndproc_other_msg_count;
typedef struct { @@ -2149,7 +2151,7 @@ static int rawinput_buffer_mouse_x(void *buffer, size_t index)
static LRESULT CALLBACK rawinputbuffer_wndproc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { - UINT i, size, count, rawinput_size, iteration = rawinputbuffer_wndproc_count++; + UINT i, size, count, rawinput_size; RAWINPUT ri; char buffer[16 * sizeof(RAWINPUT64)]; MSG message; @@ -2159,6 +2161,8 @@ static LRESULT CALLBACK rawinputbuffer_wndproc(HWND hwnd, UINT msg, WPARAM wpara
if (msg == WM_INPUT) { + UINT iteration = rawinputbuffer_wndproc_wm_input_count++; + SetLastError(0xdeadbeef); count = GetRawInputBuffer(NULL, NULL, sizeof(RAWINPUTHEADER)); ok(count == ~0U, "GetRawInputBuffer succeeded\n"); @@ -2290,19 +2294,39 @@ static LRESULT CALLBACK rawinputbuffer_wndproc(HWND hwnd, UINT msg, WPARAM wpara
return 0; } + else if (msg == WM_INPUT_DEVICE_CHANGE) + { + ok(rawinputbuffer_wndproc_wm_input_count == 0, "device change event came after input event\n"); + rawinputbuffer_wndproc_wm_input_device_change_count++; + } + else + { + rawinputbuffer_wndproc_other_msg_count++; + }
return DefWindowProcA(hwnd, msg, wparam, lparam); }
static void test_GetRawInputBuffer(void) { - unsigned int size, count, rawinput_size, header_size, scan_code; + unsigned int i, size, count, rawinput_size, header_size, scan_code, num_mice = 0; RAWINPUTDEVICE raw_devices[1]; + RAWINPUTDEVICELIST *raw_device_list; char buffer[16 * sizeof(RAWINPUT64)]; HWND hwnd; BOOL ret; POINT pt;
+ GetRawInputDeviceList(NULL, &count, sizeof(RAWINPUTDEVICELIST)); + raw_device_list = malloc(sizeof(RAWINPUTDEVICELIST) * count); + GetRawInputDeviceList(raw_device_list, &count, sizeof(RAWINPUTDEVICELIST)); + for (i = 0; i < count; i++) + { + if (raw_device_list[i].dwType == RIM_TYPEMOUSE) + num_mice++; + } + free(raw_device_list); + #define HEADER_FIELD(field) (is_wow64 ? ((RAWINPUT64 *)buffer)->header.field : ((RAWINPUT *)buffer)->header.field)
if (is_wow64) rawinput_size = sizeof(RAWINPUT64); @@ -2415,14 +2439,29 @@ static void test_GetRawInputBuffer(void) count = GetRawInputBuffer((RAWINPUT*)buffer, &size, sizeof(RAWINPUTHEADER)); ok(count == 0U, "GetRawInputBuffer returned %u\n", count);
+ raw_devices[0].dwFlags = RIDEV_REMOVE; + raw_devices[0].hwndTarget = 0; + SetLastError(0xdeadbeef); + ret = RegisterRawInputDevices(raw_devices, ARRAY_SIZE(raw_devices), sizeof(RAWINPUTDEVICE)); + ok(ret, "RegisterRawInputDevices failed\n"); + ok(GetLastError() == 0xdeadbeef, "RegisterRawInputDevices returned %08lx\n", GetLastError()); + + raw_devices[0].dwFlags = RIDEV_INPUTSINK | RIDEV_DEVNOTIFY; + raw_devices[0].hwndTarget = hwnd; + SetLastError(0xdeadbeef); + ret = RegisterRawInputDevices(raw_devices, ARRAY_SIZE(raw_devices), sizeof(RAWINPUTDEVICE)); + ok(ret, "RegisterRawInputDevices failed\n"); + ok(GetLastError() == 0xdeadbeef, "RegisterRawInputDevices returned %08lx\n", GetLastError());
- rawinputbuffer_wndproc_count = 0; + rawinputbuffer_wndproc_wm_input_count = 0; + rawinputbuffer_wndproc_wm_input_device_change_count = 0; mouse_event(MOUSEEVENTF_MOVE, 1, 0, 0, 0); mouse_event(MOUSEEVENTF_MOVE, 2, 0, 0, 0); mouse_event(MOUSEEVENTF_MOVE, 3, 0, 0, 0); mouse_event(MOUSEEVENTF_MOVE, 4, 0, 0, 0); empty_message_queue(); - ok(rawinputbuffer_wndproc_count == 2, "Spurious WM_INPUT messages\n"); + ok(rawinputbuffer_wndproc_wm_input_count == 2, "Spurious WM_INPUT messages\n"); + todo_wine ok(rawinputbuffer_wndproc_wm_input_device_change_count == num_mice, "Got %d WM_INPUT_DEVICE_CHANGE messages (expected %d)\n", rawinputbuffer_wndproc_wm_input_device_change_count, num_mice);
raw_devices[0].dwFlags = RIDEV_REMOVE; raw_devices[0].hwndTarget = 0;
From: Evan Tang etang@codeweavers.com
Windows sends a WM_INPUT_DEVICE_CHANGE for every matching connected device on registration --- dlls/user32/tests/input.c | 2 +- dlls/win32u/rawinput.c | 57 ++++++++++++++++++++++++++++++++------- 2 files changed, 48 insertions(+), 11 deletions(-)
diff --git a/dlls/user32/tests/input.c b/dlls/user32/tests/input.c index 23db1aceafd..a8fafb2e919 100644 --- a/dlls/user32/tests/input.c +++ b/dlls/user32/tests/input.c @@ -2461,7 +2461,7 @@ static void test_GetRawInputBuffer(void) mouse_event(MOUSEEVENTF_MOVE, 4, 0, 0, 0); empty_message_queue(); ok(rawinputbuffer_wndproc_wm_input_count == 2, "Spurious WM_INPUT messages\n"); - todo_wine ok(rawinputbuffer_wndproc_wm_input_device_change_count == num_mice, "Got %d WM_INPUT_DEVICE_CHANGE messages (expected %d)\n", rawinputbuffer_wndproc_wm_input_device_change_count, num_mice); + ok(rawinputbuffer_wndproc_wm_input_device_change_count == num_mice, "Got %d WM_INPUT_DEVICE_CHANGE messages (expected %d)\n", rawinputbuffer_wndproc_wm_input_device_change_count, num_mice);
raw_devices[0].dwFlags = RIDEV_REMOVE; raw_devices[0].hwndTarget = 0; diff --git a/dlls/win32u/rawinput.c b/dlls/win32u/rawinput.c index 1ddbca0896a..ad8f21cd71a 100644 --- a/dlls/win32u/rawinput.c +++ b/dlls/win32u/rawinput.c @@ -828,15 +828,15 @@ static void register_rawinput_device( const RAWINPUTDEVICE *device ) /********************************************************************** * NtUserRegisterRawInputDevices (win32u.@) */ -BOOL WINAPI NtUserRegisterRawInputDevices( const RAWINPUTDEVICE *devices, UINT device_count, UINT device_size ) +BOOL WINAPI NtUserRegisterRawInputDevices( const RAWINPUTDEVICE *device_list, UINT device_count, UINT device_size ) { struct rawinput_device *server_devices; RAWINPUTDEVICE *new_registered_devices; SIZE_T size; - BOOL ret; + BOOL ret, registering_notifications = FALSE; UINT i;
- TRACE( "devices %p, device_count %u, device_size %u.\n", devices, device_count, device_size ); + TRACE( "devices %p, device_count %u, device_size %u.\n", device_list, device_count, device_size );
if (device_size != sizeof(RAWINPUTDEVICE)) { @@ -846,23 +846,23 @@ BOOL WINAPI NtUserRegisterRawInputDevices( const RAWINPUTDEVICE *devices, UINT d
for (i = 0; i < device_count; ++i) { - TRACE( "device %u: page %#x, usage %#x, flags %#x, target %p.\n", i, devices[i].usUsagePage, - devices[i].usUsage, (int)devices[i].dwFlags, devices[i].hwndTarget ); + TRACE( "device %u: page %#x, usage %#x, flags %#x, target %p.\n", i, device_list[i].usUsagePage, + device_list[i].usUsage, (int)device_list[i].dwFlags, device_list[i].hwndTarget );
- if ((devices[i].dwFlags & RIDEV_INPUTSINK) && !devices[i].hwndTarget) + if ((device_list[i].dwFlags & RIDEV_INPUTSINK) && !device_list[i].hwndTarget) { RtlSetLastWin32Error( ERROR_INVALID_PARAMETER ); return FALSE; }
- if ((devices[i].dwFlags & RIDEV_REMOVE) && devices[i].hwndTarget) + if ((device_list[i].dwFlags & RIDEV_REMOVE) && device_list[i].hwndTarget) { RtlSetLastWin32Error( ERROR_INVALID_PARAMETER ); return FALSE; }
- if (devices[i].dwFlags & ~(RIDEV_REMOVE|RIDEV_NOLEGACY|RIDEV_INPUTSINK|RIDEV_DEVNOTIFY)) - FIXME( "Unhandled flags %#x for device %u.\n", (int)devices[i].dwFlags, i ); + if (device_list[i].dwFlags & ~(RIDEV_REMOVE|RIDEV_NOLEGACY|RIDEV_INPUTSINK|RIDEV_DEVNOTIFY)) + FIXME( "Unhandled flags %#x for device %u.\n", (int)device_list[i].dwFlags, i ); }
pthread_mutex_lock( &rawinput_mutex ); @@ -882,7 +882,7 @@ BOOL WINAPI NtUserRegisterRawInputDevices( const RAWINPUTDEVICE *devices, UINT d }
registered_devices = new_registered_devices; - for (i = 0; i < device_count; ++i) register_rawinput_device( devices + i ); + for (i = 0; i < device_count; ++i) register_rawinput_device( device_list + i );
if (!(device_count = registered_device_count)) server_devices = NULL; else if (!(server_devices = malloc( device_count * sizeof(*server_devices) ))) @@ -909,6 +909,43 @@ BOOL WINAPI NtUserRegisterRawInputDevices( const RAWINPUTDEVICE *devices, UINT d
free( server_devices );
+ /* Send WM_INPUT_DEVICE_CHANGE for existing devices when registering for notifications */ + for (i = 0; i < device_count; ++i) + { + if ((device_list[i].dwFlags & RIDEV_DEVNOTIFY) && device_list[i].hwndTarget) + { + registering_notifications = TRUE; + break; + } + } + if (registering_notifications && ret) + { + struct device *device; + rawinput_update_device_list(); + LIST_FOR_EACH_ENTRY(device, &devices, struct device, entry) + { + DWORD type = device->info.dwType; + for (i = 0; i < device_count; ++i) + { + BOOL matches = FALSE; + HWND hwnd = device_list[i].hwndTarget; + ULONG usage = MAKELONG(device_list[i].usUsagePage, usage = device_list[i].usUsage); + if (!(device_list[i].dwFlags & RIDEV_DEVNOTIFY) || !hwnd) + continue; + + if (type == RIM_TYPEMOUSE) + matches = usage == MAKELONG(HID_USAGE_PAGE_GENERIC, HID_USAGE_GENERIC_MOUSE); + else if (type == RIM_TYPEKEYBOARD) + matches = usage == MAKELONG(HID_USAGE_PAGE_GENERIC, HID_USAGE_GENERIC_KEYBOARD); + else if (type == RIM_TYPEHID) + matches = usage == MAKELONG(device->info.hid.usUsagePage, device->info.hid.usUsage); + + if (matches) + NtUserPostMessage(hwnd, WM_INPUT_DEVICE_CHANGE, GIDC_ARRIVAL, (LPARAM)device->handle); + } + } + } + pthread_mutex_unlock( &rawinput_mutex );
return ret;
This merge request was closed by Rémi Bernon.