Validating that SendInput with INPUT_HARDWARE type should be no-op.
Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=50506 Signed-off-by: Rémi Bernon rbernon@codeweavers.com ---
Supersedes: 201324-201326
In this series we use __wine_send_input to send HID device notifications by adding a RAWINPUT structure parameter to it.
The RAWINPUT structure is used in an unorthodox way here, to send the notification data, as it's not supposed to carry WM_INPUT_DEVICE_CHANGE message data in general but we would have to introduce custom structure instead otherwise.
After this series the INPUT data from the notification is already sent up to wineserver, but it is then dropped as winedevice.exe is not associated with any desktop. In later patches I intend to send the RAWINPUT data too, and implement specific processing to translate this message to the proper message sequence.
In further patches, we will use the same mechanism to send HID reports, this time properly using the RAWINPUT data to carry them, and dispatch WM_INPUT messages accordingly.
Ultimately, this RAWINPUT parameter and INPUT_HARDWARE type can also be used to send mouse and keyboard rawinput messages, replacing the custom flags that are used in the implementation currently in wine-staging.
dlls/user32/input.c | 18 ++++++ dlls/user32/tests/input.c | 122 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 140 insertions(+)
diff --git a/dlls/user32/input.c b/dlls/user32/input.c index 8992c463c48..22e53585f00 100644 --- a/dlls/user32/input.c +++ b/dlls/user32/input.c @@ -182,6 +182,24 @@ UINT WINAPI SendInput( UINT count, LPINPUT inputs, int size ) UINT i; NTSTATUS status;
+ if (size != sizeof(INPUT)) + { + SetLastError( ERROR_INVALID_PARAMETER ); + return 0; + } + + if (!count) + { + SetLastError( ERROR_INVALID_PARAMETER ); + return 0; + } + + if (!inputs) + { + SetLastError( ERROR_NOACCESS ); + return 0; + } + for (i = 0; i < count; i++) { if (inputs[i].type == INPUT_MOUSE) diff --git a/dlls/user32/tests/input.c b/dlls/user32/tests/input.c index 646a9a66eb5..f48807a27a0 100644 --- a/dlls/user32/tests/input.c +++ b/dlls/user32/tests/input.c @@ -4212,6 +4212,127 @@ static void test_UnregisterDeviceNotification(void) ok(ret == FALSE, "Unregistering NULL Device Notification returned: %d\n", ret); }
+static void test_SendInput(void) +{ + INPUT input[16]; + UINT res, i; + HWND hwnd; + MSG msg; + + hwnd = CreateWindowW( L"static", L"test", WS_OVERLAPPED, 0, 0, 100, 100, 0, 0, 0, 0 ); + ok( hwnd != 0, "CreateWindowW failed\n" ); + + ShowWindow( hwnd, SW_SHOWNORMAL ); + UpdateWindow( hwnd ); + SetForegroundWindow( hwnd ); + SetFocus( hwnd ); + empty_message_queue(); + + SetLastError( 0xdeadbeef ); + res = SendInput( 0, NULL, 0 ); + ok( res == 0 && GetLastError() == ERROR_INVALID_PARAMETER, "SendInput returned %u, error %#x\n", res, GetLastError() ); + SetLastError( 0xdeadbeef ); + res = SendInput( 1, NULL, 0 ); + ok( res == 0 && GetLastError() == ERROR_INVALID_PARAMETER, "SendInput returned %u, error %#x\n", res, GetLastError() ); + SetLastError( 0xdeadbeef ); + res = SendInput( 1, NULL, sizeof(*input) ); + ok( res == 0 && (GetLastError() == ERROR_NOACCESS || GetLastError() == ERROR_INVALID_PARAMETER), + "SendInput returned %u, error %#x\n", res, GetLastError() ); + SetLastError( 0xdeadbeef ); + res = SendInput( 0, input, sizeof(*input) ); + ok( res == 0 && GetLastError() == ERROR_INVALID_PARAMETER, "SendInput returned %u, error %#x\n", res, GetLastError() ); + SetLastError( 0xdeadbeef ); + res = SendInput( 0, NULL, sizeof(*input) ); + ok( res == 0 && GetLastError() == ERROR_INVALID_PARAMETER, "SendInput returned %u, error %#x\n", res, GetLastError() ); + + memset( input, 0, sizeof(input) ); + SetLastError( 0xdeadbeef ); + res = SendInput( 1, input, sizeof(*input) ); + ok( res == 1 && GetLastError() == 0xdeadbeef, "SendInput returned %u, error %#x\n", res, GetLastError() ); + SetLastError( 0xdeadbeef ); + res = SendInput( 16, input, sizeof(*input) ); + ok( res == 16 && GetLastError() == 0xdeadbeef, "SendInput returned %u, error %#x\n", res, GetLastError() ); + + SetLastError( 0xdeadbeef ); + res = SendInput( 1, input, 0 ); + ok( res == 0 && GetLastError() == ERROR_INVALID_PARAMETER, "SendInput returned %u, error %#x\n", res, GetLastError() ); + SetLastError( 0xdeadbeef ); + res = SendInput( 1, input, sizeof(*input) + 1 ); + ok( res == 0 && GetLastError() == ERROR_INVALID_PARAMETER, "SendInput returned %u, error %#x\n", res, GetLastError() ); + SetLastError( 0xdeadbeef ); + res = SendInput( 1, input, sizeof(*input) - 1 ); + ok( res == 0 && GetLastError() == ERROR_INVALID_PARAMETER, "SendInput returned %u, error %#x\n", res, GetLastError() ); + + for (i = 0; i < ARRAY_SIZE(input); ++i) input[i].type = INPUT_KEYBOARD; + SetLastError( 0xdeadbeef ); + res = SendInput( 16, input, offsetof( INPUT, ki ) + sizeof(KEYBDINPUT) ); + ok( res == 0 && GetLastError() == ERROR_INVALID_PARAMETER, "SendInput returned %u, error %#x\n", res, GetLastError() ); + SetLastError( 0xdeadbeef ); + res = SendInput( 16, input, sizeof(*input) ); + ok( res == 16 && GetLastError() == 0xdeadbeef, "SendInput returned %u, error %#x\n", res, GetLastError() ); + empty_message_queue(); + + for (i = 0; i < ARRAY_SIZE(input); ++i) input[i].type = INPUT_HARDWARE; + SetLastError( 0xdeadbeef ); + res = SendInput( 16, input, offsetof( INPUT, hi ) + sizeof(HARDWAREINPUT) ); + ok( res == 0 && GetLastError() == ERROR_INVALID_PARAMETER, "SendInput returned %u, error %#x\n", res, GetLastError() ); + + input[0].hi.uMsg = WM_KEYDOWN; + input[0].hi.wParamL = 0; + input[0].hi.wParamH = 'A'; + input[1].hi.uMsg = WM_KEYUP; + input[1].hi.wParamL = 0; + input[1].hi.wParamH = 'A' | 0xc000; + SetLastError( 0xdeadbeef ); + res = SendInput( 16, input, sizeof(*input) ); +#ifdef _WIN64 + todo_wine + ok( res == 0 && GetLastError() == ERROR_CALL_NOT_IMPLEMENTED, "SendInput returned %u, error %#x\n", res, GetLastError() ); +#else + ok( res == 16 && GetLastError() == 0xdeadbeef, "SendInput returned %u, error %#x\n", res, GetLastError() ); +#endif + while ((res = wait_for_message(&msg)) && msg.message == WM_TIMER) DispatchMessageA(&msg); + todo_wine ok( !res, "SendInput triggered unexpected message %#x\n", msg.message ); + empty_message_queue(); + + memset( input, 0, sizeof(input) ); + input[0].type = INPUT_HARDWARE; + input[1].type = INPUT_KEYBOARD; + input[1].ki.wVk = 'A'; + input[1].ki.dwFlags = 0; + input[2].type = INPUT_KEYBOARD; + input[2].ki.wVk = 'A'; + input[2].ki.dwFlags = KEYEVENTF_KEYUP; + SetLastError( 0xdeadbeef ); + res = SendInput( 16, input, sizeof(*input) ); +#ifdef _WIN64 + todo_wine ok( res == 0 && GetLastError() == ERROR_CALL_NOT_IMPLEMENTED, "SendInput returned %u, error %#x\n", res, GetLastError() ); + while ((res = wait_for_message(&msg)) && msg.message == WM_TIMER) DispatchMessageA(&msg); + todo_wine ok( !res, "SendInput triggered unexpected message %#x\n", msg.message ); + empty_message_queue(); +#else + ok( res == 16 && GetLastError() == 0xdeadbeef, "SendInput returned %u, error %#x\n", res, GetLastError() ); + while ((res = wait_for_message(&msg)) && msg.message == WM_TIMER) DispatchMessageA(&msg); + ok( !!res, "SendInput did not trigger any message\n" ); + todo_wine ok( msg.message == WM_KEYDOWN, "SendInput triggered unexpected message %#x\n", msg.message ); + while ((res = wait_for_message(&msg)) && msg.message == WM_TIMER) DispatchMessageA(&msg); + ok( !!res, "SendInput did not trigger any message\n" ); + todo_wine ok( msg.message == WM_KEYUP, "SendInput triggered unexpected message %#x\n", msg.message ); + empty_message_queue(); +#endif + + for (i = 0; i < ARRAY_SIZE(input); ++i) input[i].type = INPUT_HARDWARE + 1; + SetLastError( 0xdeadbeef ); + res = SendInput( 16, input, sizeof(*input) ); + todo_wine ok( res == 16 && GetLastError() == 0xdeadbeef, "SendInput returned %u, error %#x\n", res, GetLastError() ); + while ((res = wait_for_message(&msg)) && msg.message == WM_TIMER) DispatchMessageA(&msg); + todo_wine ok( !res, "SendInput triggered unexpected message %#x\n", msg.message ); + empty_message_queue(); + + trace( "done\n" ); + DestroyWindow( hwnd ); +} + START_TEST(input) { char **argv; @@ -4234,6 +4355,7 @@ START_TEST(input) return; }
+ test_SendInput(); test_Input_blackbox(); test_Input_whitebox(); test_Input_unicode();
Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=50506 Signed-off-by: Rémi Bernon rbernon@codeweavers.com --- dlls/user32/input.c | 16 ++++++++++++---- dlls/user32/tests/input.c | 15 +++++++-------- 2 files changed, 19 insertions(+), 12 deletions(-)
diff --git a/dlls/user32/input.c b/dlls/user32/input.c index 22e53585f00..e97264960ea 100644 --- a/dlls/user32/input.c +++ b/dlls/user32/input.c @@ -180,7 +180,7 @@ static void update_mouse_coords( INPUT *input ) UINT WINAPI SendInput( UINT count, LPINPUT inputs, int size ) { UINT i; - NTSTATUS status; + NTSTATUS status = STATUS_SUCCESS;
if (size != sizeof(INPUT)) { @@ -202,14 +202,22 @@ UINT WINAPI SendInput( UINT count, LPINPUT inputs, int size )
for (i = 0; i < count; i++) { - if (inputs[i].type == INPUT_MOUSE) + INPUT input = inputs[i]; + switch (input.type) { + case INPUT_MOUSE: /* we need to update the coordinates to what the server expects */ - INPUT input = inputs[i]; update_mouse_coords( &input ); + /* fallthrough */ + case INPUT_KEYBOARD: status = send_hardware_message( 0, &input, SEND_HWMSG_INJECTED ); + break; +#ifdef _WIN64 + case INPUT_HARDWARE: + SetLastError( ERROR_CALL_NOT_IMPLEMENTED ); + return 0; +#endif } - else status = send_hardware_message( 0, &inputs[i], SEND_HWMSG_INJECTED );
if (status) { diff --git a/dlls/user32/tests/input.c b/dlls/user32/tests/input.c index f48807a27a0..564ab2e1ba0 100644 --- a/dlls/user32/tests/input.c +++ b/dlls/user32/tests/input.c @@ -4286,13 +4286,12 @@ static void test_SendInput(void) SetLastError( 0xdeadbeef ); res = SendInput( 16, input, sizeof(*input) ); #ifdef _WIN64 - todo_wine ok( res == 0 && GetLastError() == ERROR_CALL_NOT_IMPLEMENTED, "SendInput returned %u, error %#x\n", res, GetLastError() ); #else ok( res == 16 && GetLastError() == 0xdeadbeef, "SendInput returned %u, error %#x\n", res, GetLastError() ); #endif while ((res = wait_for_message(&msg)) && msg.message == WM_TIMER) DispatchMessageA(&msg); - todo_wine ok( !res, "SendInput triggered unexpected message %#x\n", msg.message ); + ok( !res, "SendInput triggered unexpected message %#x\n", msg.message ); empty_message_queue();
memset( input, 0, sizeof(input) ); @@ -4306,27 +4305,27 @@ static void test_SendInput(void) SetLastError( 0xdeadbeef ); res = SendInput( 16, input, sizeof(*input) ); #ifdef _WIN64 - todo_wine ok( res == 0 && GetLastError() == ERROR_CALL_NOT_IMPLEMENTED, "SendInput returned %u, error %#x\n", res, GetLastError() ); + ok( res == 0 && GetLastError() == ERROR_CALL_NOT_IMPLEMENTED, "SendInput returned %u, error %#x\n", res, GetLastError() ); while ((res = wait_for_message(&msg)) && msg.message == WM_TIMER) DispatchMessageA(&msg); - todo_wine ok( !res, "SendInput triggered unexpected message %#x\n", msg.message ); + ok( !res, "SendInput triggered unexpected message %#x\n", msg.message ); empty_message_queue(); #else ok( res == 16 && GetLastError() == 0xdeadbeef, "SendInput returned %u, error %#x\n", res, GetLastError() ); while ((res = wait_for_message(&msg)) && msg.message == WM_TIMER) DispatchMessageA(&msg); ok( !!res, "SendInput did not trigger any message\n" ); - todo_wine ok( msg.message == WM_KEYDOWN, "SendInput triggered unexpected message %#x\n", msg.message ); + ok( msg.message == WM_KEYDOWN, "SendInput triggered unexpected message %#x\n", msg.message ); while ((res = wait_for_message(&msg)) && msg.message == WM_TIMER) DispatchMessageA(&msg); ok( !!res, "SendInput did not trigger any message\n" ); - todo_wine ok( msg.message == WM_KEYUP, "SendInput triggered unexpected message %#x\n", msg.message ); + ok( msg.message == WM_KEYUP, "SendInput triggered unexpected message %#x\n", msg.message ); empty_message_queue(); #endif
for (i = 0; i < ARRAY_SIZE(input); ++i) input[i].type = INPUT_HARDWARE + 1; SetLastError( 0xdeadbeef ); res = SendInput( 16, input, sizeof(*input) ); - todo_wine ok( res == 16 && GetLastError() == 0xdeadbeef, "SendInput returned %u, error %#x\n", res, GetLastError() ); + ok( res == 16 && GetLastError() == 0xdeadbeef, "SendInput returned %u, error %#x\n", res, GetLastError() ); while ((res = wait_for_message(&msg)) && msg.message == WM_TIMER) DispatchMessageA(&msg); - todo_wine ok( !res, "SendInput triggered unexpected message %#x\n", msg.message ); + ok( !res, "SendInput triggered unexpected message %#x\n", msg.message ); empty_message_queue();
trace( "done\n" );
Hi,
While running your changed tests, I think I found new failures. Being a bot and all I'm not very good at pattern recognition, so I might be wrong, but could you please double-check?
Full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=88422
Your paranoid android.
=== w7u_2qxl (32 bit report) ===
user32: input.c:756: Test failed: 0 (a4/0): 00 from 00 -> 80 unexpected input.c:756: Test failed: 0 (a4/0): 41 from 01 -> 00 unexpected
=== w7u_el (32 bit report) ===
user32: input.c:4294: Test failed: SendInput triggered unexpected message 0xc042
=== wvistau64 (64 bit report) ===
user32: input.c:756: Test failed: 0 (a4/0): 00 from 00 -> 80 unexpected input.c:756: Test failed: 0 (a4/0): 01 from 01 -> 00 unexpected input.c:756: Test failed: 0 (a4/0): 11 from 01 -> 00 unexpected input.c:756: Test failed: 0 (a4/0): a2 from 01 -> 00 unexpected
=== debiant2 (32 bit report) ===
user32: win.c:10119: Test failed: Expected foreground window 000E013E, got 00E10102 win.c:10121: Test failed: GetActiveWindow() = 00000000 win.c:10121: Test failed: GetFocus() = 00000000 win.c:10122: Test failed: Received WM_ACTIVATEAPP(1), did not expect it. win.c:10123: Test failed: Received WM_ACTIVATEAPP(0), did not expect it. win.c:10131: Test failed: Expected foreground window 000E013E, got 00000000 win.c:10133: Test failed: GetActiveWindow() = 00000000 win.c:10133: Test failed: GetFocus() = 00000000 win.c:10141: Test failed: Received WM_ACTIVATEAPP(1), did not expect it.
=== debiant2 (32 bit Chinese:China report) ===
user32: clipboard.c:330: Test failed: OpenClipboard error 5 clipboard.c:77: Test failed: 331: SetClipboardData failed clipboard.c:79: Test failed: 331: SetClipboardData failed err 1418 clipboard.c:332: Test failed: CF_WAVE not available clipboard.c:333: Test failed: CF_WAVE data not available clipboard.c:107: Test failed: process 3: CF_WAVE not available clipboard.c:109: Test failed: process 3: SetClipboardData failed err 1418 clipboard.c:336: Test failed: CloseClipboard error 1418
And send_hardware_message.
This makes it possible to use __wine_send_input to send extended input data, such as HID device notifications and WM_INPUT messages carrying HID reports.
Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=50506 Signed-off-by: Rémi Bernon rbernon@codeweavers.com --- dlls/user32/input.c | 6 +++--- dlls/user32/message.c | 2 +- dlls/user32/user32.spec | 2 +- dlls/user32/user_private.h | 2 +- dlls/wineandroid.drv/keyboard.c | 2 +- dlls/wineandroid.drv/window.c | 4 ++-- dlls/winemac.drv/ime.c | 4 ++-- dlls/winemac.drv/keyboard.c | 2 +- dlls/winemac.drv/mouse.c | 2 +- dlls/winex11.drv/keyboard.c | 2 +- dlls/winex11.drv/mouse.c | 8 ++++---- include/winuser.h | 2 +- 12 files changed, 19 insertions(+), 19 deletions(-)
diff --git a/dlls/user32/input.c b/dlls/user32/input.c index e97264960ea..3fc818a2510 100644 --- a/dlls/user32/input.c +++ b/dlls/user32/input.c @@ -119,9 +119,9 @@ BOOL set_capture_window( HWND hwnd, UINT gui_flags, HWND *prev_ret ) * * Internal SendInput function to allow the graphics driver to inject real events. */ -BOOL CDECL __wine_send_input( HWND hwnd, const INPUT *input ) +BOOL CDECL __wine_send_input( HWND hwnd, const INPUT *input, const RAWINPUT *rawinput ) { - NTSTATUS status = send_hardware_message( hwnd, input, 0 ); + NTSTATUS status = send_hardware_message( hwnd, input, rawinput, 0 ); if (status) SetLastError( RtlNtStatusToDosError(status) ); return !status; } @@ -210,7 +210,7 @@ UINT WINAPI SendInput( UINT count, LPINPUT inputs, int size ) update_mouse_coords( &input ); /* fallthrough */ case INPUT_KEYBOARD: - status = send_hardware_message( 0, &input, SEND_HWMSG_INJECTED ); + status = send_hardware_message( 0, &input, NULL, SEND_HWMSG_INJECTED ); break; #ifdef _WIN64 case INPUT_HARDWARE: diff --git a/dlls/user32/message.c b/dlls/user32/message.c index def59998a52..f87ef9fb3af 100644 --- a/dlls/user32/message.c +++ b/dlls/user32/message.c @@ -3227,7 +3227,7 @@ static BOOL send_message( struct send_message_info *info, DWORD_PTR *res_ptr, BO /*********************************************************************** * send_hardware_message */ -NTSTATUS send_hardware_message( HWND hwnd, const INPUT *input, UINT flags ) +NTSTATUS send_hardware_message( HWND hwnd, const INPUT *input, const RAWINPUT *rawinput, UINT flags ) { struct user_key_state_info *key_state_info = get_user_thread_info()->key_state; struct send_message_info info; diff --git a/dlls/user32/user32.spec b/dlls/user32/user32.spec index 4ef75247d71..190ee74fd6c 100644 --- a/dlls/user32/user32.spec +++ b/dlls/user32/user32.spec @@ -834,5 +834,5 @@ # All functions must be prefixed with '__wine_' (for internal functions) # or 'wine_' (for user-visible functions) to avoid namespace conflicts. # -@ cdecl __wine_send_input(long ptr) +@ cdecl __wine_send_input(long ptr ptr) @ cdecl __wine_set_pixel_format(long long) diff --git a/dlls/user32/user_private.h b/dlls/user32/user_private.h index 7761a1ceb4f..dfd52421e66 100644 --- a/dlls/user32/user_private.h +++ b/dlls/user32/user_private.h @@ -263,7 +263,7 @@ extern RECT get_virtual_screen_rect(void) DECLSPEC_HIDDEN; extern LRESULT call_current_hook( HHOOK hhook, INT code, WPARAM wparam, LPARAM lparam ) DECLSPEC_HIDDEN; extern DWORD get_input_codepage( void ) DECLSPEC_HIDDEN; extern BOOL map_wparam_AtoW( UINT message, WPARAM *wparam, enum wm_char_mapping mapping ) DECLSPEC_HIDDEN; -extern NTSTATUS send_hardware_message( HWND hwnd, const INPUT *input, UINT flags ) DECLSPEC_HIDDEN; +extern NTSTATUS send_hardware_message( HWND hwnd, const INPUT *input, const RAWINPUT *rawinput, UINT flags ) DECLSPEC_HIDDEN; extern LRESULT MSG_SendInternalMessageTimeout( DWORD dest_pid, DWORD dest_tid, UINT msg, WPARAM wparam, LPARAM lparam, UINT flags, UINT timeout, PDWORD_PTR res_ptr ) DECLSPEC_HIDDEN; diff --git a/dlls/wineandroid.drv/keyboard.c b/dlls/wineandroid.drv/keyboard.c index 1c8a1e4f68f..0a6ede0ec5f 100644 --- a/dlls/wineandroid.drv/keyboard.c +++ b/dlls/wineandroid.drv/keyboard.c @@ -680,7 +680,7 @@ static void send_keyboard_input( HWND hwnd, WORD vkey, WORD scan, DWORD flags ) input.u.ki.time = 0; input.u.ki.dwExtraInfo = 0;
- __wine_send_input( hwnd, &input ); + __wine_send_input( hwnd, &input, NULL ); }
/*********************************************************************** diff --git a/dlls/wineandroid.drv/window.c b/dlls/wineandroid.drv/window.c index 79bc471a984..1cb1bbbadc9 100644 --- a/dlls/wineandroid.drv/window.c +++ b/dlls/wineandroid.drv/window.c @@ -521,7 +521,7 @@ static int process_events( DWORD mask ) } SERVER_END_REQ; } - __wine_send_input( capture ? capture : event->data.motion.hwnd, &event->data.motion.input ); + __wine_send_input( capture ? capture : event->data.motion.hwnd, &event->data.motion.input, NULL ); } break;
@@ -535,7 +535,7 @@ static int process_events( DWORD mask ) event->data.kbd.input.u.ki.wVk, event->data.kbd.input.u.ki.wVk, event->data.kbd.input.u.ki.wScan ); update_keyboard_lock_state( event->data.kbd.input.u.ki.wVk, event->data.kbd.lock_state ); - __wine_send_input( 0, &event->data.kbd.input ); + __wine_send_input( 0, &event->data.kbd.input, NULL ); break;
default: diff --git a/dlls/winemac.drv/ime.c b/dlls/winemac.drv/ime.c index dabe6654f98..f2368c10743 100644 --- a/dlls/winemac.drv/ime.c +++ b/dlls/winemac.drv/ime.c @@ -1427,10 +1427,10 @@ void macdrv_im_set_text(const macdrv_event *event) { input.ki.wScan = chars[i]; input.ki.dwFlags = KEYEVENTF_UNICODE; - __wine_send_input(hwnd, &input); + __wine_send_input(hwnd, &input, NULL);
input.ki.dwFlags = KEYEVENTF_UNICODE | KEYEVENTF_KEYUP; - __wine_send_input(hwnd, &input); + __wine_send_input(hwnd, &input, NULL); } }
diff --git a/dlls/winemac.drv/keyboard.c b/dlls/winemac.drv/keyboard.c index 1b74300e93a..1ea15f59341 100644 --- a/dlls/winemac.drv/keyboard.c +++ b/dlls/winemac.drv/keyboard.c @@ -929,7 +929,7 @@ static void macdrv_send_keyboard_input(HWND hwnd, WORD vkey, WORD scan, DWORD fl input.ki.time = time; input.ki.dwExtraInfo = 0;
- __wine_send_input(hwnd, &input); + __wine_send_input(hwnd, &input, NULL); }
diff --git a/dlls/winemac.drv/mouse.c b/dlls/winemac.drv/mouse.c index dd6443fe1ba..d2278ae0e4c 100644 --- a/dlls/winemac.drv/mouse.c +++ b/dlls/winemac.drv/mouse.c @@ -165,7 +165,7 @@ static void send_mouse_input(HWND hwnd, macdrv_window cocoa_window, UINT flags, input.mi.time = time; input.mi.dwExtraInfo = 0;
- __wine_send_input(top_level_hwnd, &input); + __wine_send_input(top_level_hwnd, &input, NULL); }
diff --git a/dlls/winex11.drv/keyboard.c b/dlls/winex11.drv/keyboard.c index 35a801fc895..01620c5e4a4 100644 --- a/dlls/winex11.drv/keyboard.c +++ b/dlls/winex11.drv/keyboard.c @@ -1148,7 +1148,7 @@ static void X11DRV_send_keyboard_input( HWND hwnd, WORD vkey, WORD scan, DWORD f input.u.ki.time = time; input.u.ki.dwExtraInfo = 0;
- __wine_send_input( hwnd, &input ); + __wine_send_input( hwnd, &input, NULL ); }
diff --git a/dlls/winex11.drv/mouse.c b/dlls/winex11.drv/mouse.c index 94dece652b6..42bac332664 100644 --- a/dlls/winex11.drv/mouse.c +++ b/dlls/winex11.drv/mouse.c @@ -659,7 +659,7 @@ static void send_mouse_input( HWND hwnd, Window window, unsigned int state, INPU sync_window_cursor( window ); last_cursor_change = input->u.mi.time; } - __wine_send_input( hwnd, input ); + __wine_send_input( hwnd, input, NULL ); return; }
@@ -699,7 +699,7 @@ static void send_mouse_input( HWND hwnd, Window window, unsigned int state, INPU SERVER_END_REQ; }
- __wine_send_input( hwnd, input ); + __wine_send_input( hwnd, input, NULL ); }
#ifdef SONAME_LIBXCURSOR @@ -1669,7 +1669,7 @@ void move_resize_window( HWND hwnd, int dir ) input.u.mi.dwFlags = button_up_flags[button - 1] | MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_MOVE; input.u.mi.time = GetTickCount(); input.u.mi.dwExtraInfo = 0; - __wine_send_input( hwnd, &input ); + __wine_send_input( hwnd, &input, NULL ); }
while (PeekMessageW( &msg, 0, 0, 0, PM_REMOVE )) @@ -1900,7 +1900,7 @@ static BOOL X11DRV_RawMotion( XGenericEventCookie *xev ) TRACE( "pos %d,%d (event %f,%f)\n", input.u.mi.dx, input.u.mi.dy, dx, dy );
input.type = INPUT_MOUSE; - __wine_send_input( 0, &input ); + __wine_send_input( 0, &input, NULL ); return TRUE; }
diff --git a/include/winuser.h b/include/winuser.h index 53661f6c788..0b1571c0a95 100644 --- a/include/winuser.h +++ b/include/winuser.h @@ -4406,7 +4406,7 @@ static inline BOOL WINAPI SetRectEmpty(LPRECT rect) WORD WINAPI SYSTEM_KillSystemTimer( WORD );
#ifdef __WINESRC__ -WINUSERAPI BOOL CDECL __wine_send_input( HWND hwnd, const INPUT *input ); +WINUSERAPI BOOL CDECL __wine_send_input( HWND hwnd, const INPUT *input, const RAWINPUT *rawinput ); #endif
#ifdef __cplusplus
Hi,
While running your changed tests, I think I found new failures. Being a bot and all I'm not very good at pattern recognition, so I might be wrong, but could you please double-check?
Full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=88423
Your paranoid android.
=== debiant2 (32 bit Chinese:China report) ===
user32: clipboard.c:1168: Test failed: OpenClipboard failed: 5 clipboard.c:1170: Test failed: EmptyClipboard failed: 1418 clipboard.c:1172: Test failed: CloseClipboard failed: 1418 clipboard.c:1174: Test failed: sequence diff 0 clipboard.c:1017: Test failed: wait failed clipboard.c:1174: Test failed: WM_DRAWCLIPBOARD not received clipboard.c:1174: Test failed: WM_CLIPBOARDUPDATE not received clipboard.c:1199: Test failed: WM_DESTROYCLIPBOARD received
=== debiant2 (64 bit WoW report) ===
user32: win.c:10096: Test failed: Expected foreground window 0, got 0000000000CF00CC win.c:10102: Test failed: Expected foreground window 00000000000E013E, got 0000000000CF00CC
The handles are just numeric values and not real object handles, they are used in the hDevice field of the RAWINPUTHEADER struct.
They will also be used as an HID rawinput device array index on the server side.
Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=50506 Signed-off-by: Rémi Bernon rbernon@codeweavers.com --- dlls/hidclass.sys/device.c | 17 +++++++++++++++++ dlls/hidclass.sys/hid.h | 1 + 2 files changed, 18 insertions(+)
diff --git a/dlls/hidclass.sys/device.c b/dlls/hidclass.sys/device.c index fc1dfd07db1..9a3c92b3576 100644 --- a/dlls/hidclass.sys/device.c +++ b/dlls/hidclass.sys/device.c @@ -71,6 +71,17 @@ NTSTATUS HID_CreateDevice(DEVICE_OBJECT *native_device, HID_MINIDRIVER_REGISTRAT return STATUS_SUCCESS; }
+/* user32 reserves 1 & 2 for winemouse and winekeyboard, + * keep this in sync with user_private.h */ +#define WINE_MOUSE_HANDLE 1 +#define WINE_KEYBOARD_HANDLE 2 + +static UINT32 alloc_rawinput_handle(void) +{ + static LONG counter = WINE_KEYBOARD_HANDLE + 1; + return InterlockedIncrement(&counter); +} + NTSTATUS HID_LinkDevice(DEVICE_OBJECT *device) { WCHAR device_instance_id[MAX_DEVICE_ID_LEN]; @@ -125,7 +136,13 @@ NTSTATUS HID_LinkDevice(DEVICE_OBJECT *device) { if (!IoRegisterDeviceInterface(device, &GUID_DEVINTERFACE_MOUSE, NULL, &ext->mouse_link_name)) ext->is_mouse = TRUE; + ext->rawinput_handle = WINE_MOUSE_HANDLE; } + else if (ext->preparseData->caps.UsagePage == HID_USAGE_PAGE_GENERIC + && ext->preparseData->caps.Usage == HID_USAGE_GENERIC_KEYBOARD) + ext->rawinput_handle = WINE_KEYBOARD_HANDLE; + else + ext->rawinput_handle = alloc_rawinput_handle();
return STATUS_SUCCESS;
diff --git a/dlls/hidclass.sys/hid.h b/dlls/hidclass.sys/hid.h index 889b8c625c0..41f3766a535 100644 --- a/dlls/hidclass.sys/hid.h +++ b/dlls/hidclass.sys/hid.h @@ -51,6 +51,7 @@ typedef struct _BASE_DEVICE_EXTENSION { struct ReportRingBuffer *ring_buffer; HANDLE halt_event; HANDLE thread; + UINT32 rawinput_handle;
KSPIN_LOCK irp_queue_lock; LIST_ENTRY irp_queue;
This currently does nothing, because winedevice.exe isn't associated with any desktop, and the INPUT_HARDWARE messages are dropped.
In this specific case, when INPUT type is INPUT_HARDWARE and hi.uMsg is WM_INPUT_DEVICE_CHANGE, the RAWINPUT structure usage is a non-standard extension for Wine internal usage:
* header.wParam contains the message GIDC_ARRIVAL / GIDC_REMOVAL wparam,
* hid.bRawData contains two bytes, which are the HID device UsagePage and Usage bytes, instead of a real HID report.
This will let us use the same entry point and structures to send device notifications as for the HID reports in the future (which will be sent with INPUT_HARDWARE type / WM_INPUT uMsg instead).
Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=50506 Signed-off-by: Rémi Bernon rbernon@codeweavers.com --- dlls/hidclass.sys/Makefile.in | 2 +- dlls/hidclass.sys/pnp.c | 36 +++++++++++++++++++++++++++++++++++ 2 files changed, 37 insertions(+), 1 deletion(-)
diff --git a/dlls/hidclass.sys/Makefile.in b/dlls/hidclass.sys/Makefile.in index be4af747853..2f9f30f8bef 100644 --- a/dlls/hidclass.sys/Makefile.in +++ b/dlls/hidclass.sys/Makefile.in @@ -1,6 +1,6 @@ MODULE = hidclass.sys IMPORTLIB = hidclass -IMPORTS = hal ntoskrnl +IMPORTS = hal ntoskrnl user32 DELAYIMPORTS = setupapi hid
EXTRADLLFLAGS = -mno-cygwin diff --git a/dlls/hidclass.sys/pnp.c b/dlls/hidclass.sys/pnp.c index a499aec93bb..8e539dfe6b7 100644 --- a/dlls/hidclass.sys/pnp.c +++ b/dlls/hidclass.sys/pnp.c @@ -25,6 +25,7 @@ #include "ddk/hidtypes.h" #include "ddk/wdm.h" #include "regstr.h" +#include "winuser.h" #include "wine/debug.h" #include "wine/list.h"
@@ -69,6 +70,9 @@ static NTSTATUS get_device_id(DEVICE_OBJECT *device, BUS_QUERY_ID_TYPE type, WCH return status; }
+/* make sure bRawData can hold two bytes without requiring additional allocation */ +C_ASSERT(offsetof(RAWINPUT, data.hid.bRawData[2]) < sizeof(RAWINPUT)); + NTSTATUS WINAPI PNP_AddDevice(DRIVER_OBJECT *driver, DEVICE_OBJECT *PDO) { WCHAR device_id[MAX_DEVICE_ID_LEN], instance_id[MAX_DEVICE_ID_LEN]; @@ -79,6 +83,8 @@ NTSTATUS WINAPI PNP_AddDevice(DRIVER_OBJECT *driver, DEVICE_OBJECT *PDO) BASE_DEVICE_EXTENSION *ext = NULL; HID_DESCRIPTOR descriptor; BYTE *reportDescriptor; + RAWINPUT rawinput; + INPUT input; INT i;
if ((status = get_device_id(PDO, BusQueryDeviceID, device_id))) @@ -187,6 +193,21 @@ NTSTATUS WINAPI PNP_AddDevice(DRIVER_OBJECT *driver, DEVICE_OBJECT *PDO)
HID_StartDeviceThread(device);
+ rawinput.header.dwType = RIM_TYPEHID; + rawinput.header.dwSize = offsetof(RAWINPUT, data.hid.bRawData[2]); + rawinput.header.hDevice = ULongToHandle(ext->rawinput_handle); + rawinput.header.wParam = GIDC_ARRIVAL; + rawinput.data.hid.dwCount = 1; + rawinput.data.hid.dwSizeHid = 2; + rawinput.data.hid.bRawData[0] = ext->preparseData->caps.UsagePage; + rawinput.data.hid.bRawData[1] = ext->preparseData->caps.Usage; + + input.type = INPUT_HARDWARE; + input.u.hi.uMsg = WM_INPUT_DEVICE_CHANGE; + input.u.hi.wParamH = (WORD)(rawinput.header.dwSize >> 16); + input.u.hi.wParamL = (WORD)(rawinput.header.dwSize >> 0); + __wine_send_input(0, &input, &rawinput); + return STATUS_SUCCESS; }
@@ -194,6 +215,21 @@ static NTSTATUS remove_device(minidriver *minidriver, DEVICE_OBJECT *device, IRP { BASE_DEVICE_EXTENSION *ext = device->DeviceExtension; NTSTATUS rc = STATUS_NOT_SUPPORTED; + RAWINPUT rawinput; + INPUT input; + + rawinput.header.dwType = RIM_TYPEHID; + rawinput.header.dwSize = offsetof(RAWINPUT, data.hid.bRawData[0]); + rawinput.header.hDevice = ULongToHandle(ext->rawinput_handle); + rawinput.header.wParam = GIDC_REMOVAL; + rawinput.data.hid.dwCount = 0; + rawinput.data.hid.dwSizeHid = 0; + + input.type = INPUT_HARDWARE; + input.u.hi.uMsg = WM_INPUT_DEVICE_CHANGE; + input.u.hi.wParamH = (WORD)(rawinput.header.dwSize >> 16); + input.u.hi.wParamL = (WORD)(rawinput.header.dwSize >> 0); + __wine_send_input(0, &input, &rawinput);
rc = IoSetDeviceInterfaceState(&ext->link_name, FALSE); if (rc)
Hi,
While running your changed tests, I think I found new failures. Being a bot and all I'm not very good at pattern recognition, so I might be wrong, but could you please double-check?
Full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=88421
Your paranoid android.
=== w7u_2qxl (32 bit report) ===
user32: input.c:756: Test failed: 0 (a4/0): 00 from 00 -> 80 unexpected input.c:756: Test failed: 0 (a4/0): 41 from 01 -> 00 unexpected
=== wvistau64 (64 bit report) ===
user32: input.c:756: Test failed: 0 (a4/0): 00 from 00 -> 80 unexpected input.c:756: Test failed: 0 (a4/0): 01 from 01 -> 00 unexpected input.c:756: Test failed: 0 (a4/0): 11 from 01 -> 00 unexpected input.c:756: Test failed: 0 (a4/0): a2 from 01 -> 00 unexpected
=== debiant2 (64 bit WoW report) ===
user32: win.c:10096: Test failed: Expected foreground window 0, got 0000000000CF00CC win.c:10102: Test failed: Expected foreground window 00000000000E013E, got 0000000000CF00CC
Rémi Bernon rbernon@codeweavers.com writes:
+#ifdef _WIN64
- todo_wine
- ok( res == 0 && GetLastError() == ERROR_CALL_NOT_IMPLEMENTED, "SendInput returned %u, error %#x\n", res, GetLastError() );
+#else
- ok( res == 16 && GetLastError() == 0xdeadbeef, "SendInput returned %u, error %#x\n", res, GetLastError() );
+#endif
I doubt that there's anything 64-bit specific about this. Most likely that's another case of imperfect WoW64 translation that wouldn't appear on pure 32-bit. I don't think we want to add #ifdefs for this.
On 4/8/21 7:01 PM, Alexandre Julliard wrote:
Rémi Bernon rbernon@codeweavers.com writes:
+#ifdef _WIN64
- todo_wine
- ok( res == 0 && GetLastError() == ERROR_CALL_NOT_IMPLEMENTED, "SendInput returned %u, error %#x\n", res, GetLastError() );
+#else
- ok( res == 16 && GetLastError() == 0xdeadbeef, "SendInput returned %u, error %#x\n", res, GetLastError() );
+#endif
I doubt that there's anything 64-bit specific about this. Most likely that's another case of imperfect WoW64 translation that wouldn't appear on pure 32-bit. I don't think we want to add #ifdefs for this.
I don't mind re-sending with the 32bit tests marked as broken, but it seemed to me that there was a difference. As I understand it this is some legacy thing and I guess that was cleaned up at some point to return genuine error codes indicating that it does nothing now.
For instance, the 32bit tests running on winxp here:
https://testbot.winehq.org/JobDetails.pl?Key=88479