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?
-- v2: 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 | 38 ++++++++++++++++++++++++++++++++------ 1 file changed, 32 insertions(+), 6 deletions(-)
diff --git a/dlls/user32/tests/input.c b/dlls/user32/tests/input.c index f8b40099091..b0c4b699141 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); @@ -2320,7 +2344,7 @@ static void test_GetRawInputBuffer(void)
raw_devices[0].usUsagePage = 0x01; raw_devices[0].usUsage = 0x02; - raw_devices[0].dwFlags = RIDEV_INPUTSINK; + raw_devices[0].dwFlags = RIDEV_INPUTSINK | RIDEV_DEVNOTIFY; raw_devices[0].hwndTarget = hwnd;
SetLastError(0xdeadbeef); @@ -2416,13 +2440,15 @@ static void test_GetRawInputBuffer(void) ok(count == 0U, "GetRawInputBuffer returned %u\n", count);
- 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 | 46 +++++++++++++++++++++++++++++++-------- 2 files changed, 38 insertions(+), 10 deletions(-)
diff --git a/dlls/user32/tests/input.c b/dlls/user32/tests/input.c index b0c4b699141..eb6419d7122 100644 --- a/dlls/user32/tests/input.c +++ b/dlls/user32/tests/input.c @@ -2448,7 +2448,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..2240237b378 100644 --- a/dlls/win32u/rawinput.c +++ b/dlls/win32u/rawinput.c @@ -828,7 +828,7 @@ 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; @@ -836,7 +836,7 @@ BOOL WINAPI NtUserRegisterRawInputDevices( const RAWINPUTDEVICE *devices, UINT d BOOL ret; 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,34 @@ BOOL WINAPI NtUserRegisterRawInputDevices( const RAWINPUTDEVICE *devices, UINT d
free( server_devices );
+ if (ret) + { + // Send WM_INPUT_DEVICE_CHANGE for existing devices when registering for notifications + for (i = 0; i < device_count; ++i) + { + HWND hwnd = device_list[i].hwndTarget; + USHORT page = device_list[i].usUsagePage, usage = device_list[i].usUsage; + struct device *device; + if (!(device_list[i].dwFlags & RIDEV_DEVNOTIFY) || !hwnd) + continue; + + rawinput_update_device_list(); + LIST_FOR_EACH_ENTRY(device, &devices, struct device, entry) + { + BOOL matches = FALSE; + if (device->info.dwType == RIM_TYPEMOUSE) + matches = page == HID_USAGE_PAGE_GENERIC && usage == HID_USAGE_GENERIC_MOUSE; + else if (device->info.dwType == RIM_TYPEKEYBOARD) + matches = page == HID_USAGE_PAGE_GENERIC && usage == HID_USAGE_GENERIC_KEYBOARD; + else if (device->info.dwType == RIM_TYPEHID) + matches = page == device->info.hid.usUsagePage && usage == device->info.hid.usUsage; + + if (matches) + NtUserPostMessage(hwnd, WM_INPUT_DEVICE_CHANGE, GIDC_ARRIVAL, (LPARAM)device->handle); + } + } + } + pthread_mutex_unlock( &rawinput_mutex );
return ret;
Hi,
It looks like your patch introduced the new failures shown below. Please investigate and fix them before resubmitting your patch. If they are not new, fixing them anyway would help a lot. Otherwise please ask for the known failures list to be updated.
The tests also ran into some preexisting test failures. If you know how to fix them that would be helpful. See the TestBot job for the details:
The full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=129141
Your paranoid android.
=== w7u_2qxl (32 bit report) ===
user32: input.c:2367: Test failed: GetRawInputBuffer returned 4294967295
=== w7u_adm (32 bit report) ===
user32: input.c:2367: Test failed: GetRawInputBuffer returned 4294967295
=== w7u_el (32 bit report) ===
user32: input.c:2367: Test failed: GetRawInputBuffer returned 4294967295
=== w8 (32 bit report) ===
user32: input.c:2367: Test failed: GetRawInputBuffer returned 4294967295
=== w8adm (32 bit report) ===
user32: input.c:2367: Test failed: GetRawInputBuffer returned 4294967295
=== w864 (32 bit report) ===
user32: input.c:2367: Test failed: GetRawInputBuffer returned 4294967295
=== w1064v1507 (32 bit report) ===
user32: input.c:2367: Test failed: GetRawInputBuffer returned 4294967295
=== w1064v1809 (32 bit report) ===
user32: input.c:2367: Test failed: GetRawInputBuffer returned 4294967295
=== w1064_tsign (32 bit report) ===
user32: input.c:2367: Test failed: GetRawInputBuffer returned 4294967295
=== w10pro64 (32 bit report) ===
user32: input.c:2367: Test failed: GetRawInputBuffer returned 4294967295
=== w864 (64 bit report) ===
user32: input.c:2367: Test failed: GetRawInputBuffer returned 4294967295
=== w1064v1507 (64 bit report) ===
user32: input.c:2367: Test failed: GetRawInputBuffer returned 4294967295
=== w1064v1809 (64 bit report) ===
user32: input.c:2367: Test failed: GetRawInputBuffer returned 4294967295
=== w1064_2qxl (64 bit report) ===
user32: input.c:2367: Test failed: GetRawInputBuffer returned 4294967295
=== w1064_adm (64 bit report) ===
user32: input.c:2367: Test failed: GetRawInputBuffer returned 4294967295
=== w1064_tsign (64 bit report) ===
user32: input.c:2367: Test failed: GetRawInputBuffer returned 4294967295
=== w10pro64 (64 bit report) ===
user32: input.c:2367: Test failed: GetRawInputBuffer returned 4294967295
=== w10pro64_en_AE_u8 (64 bit report) ===
user32: input.c:2367: Test failed: GetRawInputBuffer returned 4294967295
=== w10pro64_ar (64 bit report) ===
user32: input.c:2367: Test failed: GetRawInputBuffer returned 4294967295
=== w10pro64_ja (64 bit report) ===
user32: input.c:2367: Test failed: GetRawInputBuffer returned 4294967295
=== w10pro64_zh_CN (64 bit report) ===
user32: input.c:2367: Test failed: GetRawInputBuffer returned 4294967295
Rémi Bernon (@rbernon) commented about dlls/win32u/rawinput.c:
free( server_devices );
- if (ret)
- {
// Send WM_INPUT_DEVICE_CHANGE for existing devices when registering for notifications
Please use C-style comments. Also regarding style, please keep it consistent with the code around, ie: use spaces inside function and macros parentheses.
Rémi Bernon (@rbernon) commented about dlls/win32u/rawinput.c:
/**********************************************************************
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 )
I'm not sure it's really useful to rename the variable. At least, doing it in this quite unrelated change, only adds noise.
Rémi Bernon (@rbernon) commented about dlls/win32u/rawinput.c:
rawinput_update_device_list();
LIST_FOR_EACH_ENTRY(device, &devices, struct device, entry)
{
BOOL matches = FALSE;
if (device->info.dwType == RIM_TYPEMOUSE)
matches = page == HID_USAGE_PAGE_GENERIC && usage == HID_USAGE_GENERIC_MOUSE;
else if (device->info.dwType == RIM_TYPEKEYBOARD)
matches = page == HID_USAGE_PAGE_GENERIC && usage == HID_USAGE_GENERIC_KEYBOARD;
else if (device->info.dwType == RIM_TYPEHID)
matches = page == device->info.hid.usUsagePage && usage == device->info.hid.usUsage;
if (matches)
NtUserPostMessage(hwnd, WM_INPUT_DEVICE_CHANGE, GIDC_ARRIVAL, (LPARAM)device->handle);
}
}
I'm not completely sure we should do this here, but at the same time I don't think we can do it on the server side right now as it doesn't keep a list of devices.
In any case you should invert the loops, refreshing the device list once, and looking up the client device array for each of the device list iteration: looking up the array is fast as it's sequential (and probably small), whereas iterating the list is slow.
I think you could use `MAKELONG` to combine usage and usage page together and make the comparison easier.
Regarding the device types I don't think there's anything more to do, we don't really support mouse-like or keyboard-like HID devices at the moment.
On Tue Feb 7 10:05:15 2023 +0000, Rémi Bernon wrote:
I'm not completely sure we should do this here, but at the same time I don't think we can do it on the server side right now as it doesn't keep a list of devices. In any case you should invert the loops, refreshing the device list once, and looking up the client device array for each of the device list iteration: looking up the array is fast as it's sequential (and probably small), whereas iterating the list is slow. I think you could use `MAKELONG` to combine usage and usage page together and make the comparison easier. Regarding the device types I don't think there's anything more to do, we don't really support mouse-like or keyboard-like HID devices at the moment.
I'll need to think a bit about whether this is right. I also don't know if posting the message is alright, or if this should use `Send(Notify)Message` or `__wine_send_input` instead somehow.