The first commit adds some test infrastructure to be able check raw input keyboard events, along with a few tests relevant to the code paths in this MR.
The second commit fixes raw input behavior with unicode inputs, and adds regression tests. Note that it was difficult to express the current Wine behavior with TODOs in the expected sequence, that's why I didn't introduce these regression tests independently.
-- v2: server: Set VK_PACKET async state in raw input legacy mode. user32: Check async key state in raw nolegacy tests. server: Don't send raw input events for unicode inputs. server: Apply modifier vkey transformations regardless of unicode flag. server: Use right-left modifier vkeys for hooks. user32: Add tests for raw keyboard messages. user32: Add more test for unicode input with vkey.
From: Alexandros Frantzis alexandros.frantzis@collabora.com
--- dlls/user32/tests/input.c | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+)
diff --git a/dlls/user32/tests/input.c b/dlls/user32/tests/input.c index c123f8fa439..f21b9e24555 100644 --- a/dlls/user32/tests/input.c +++ b/dlls/user32/tests/input.c @@ -1169,6 +1169,25 @@ static void test_SendInput_keyboard_messages( WORD vkey, WORD scan, WCHAR wch, W {0}, };
+ struct send_input_keyboard_test unicode_vkey_ctrl[] = + { + {.scan = 0x3c0, .vkey = VK_CONTROL, .flags = KEYEVENTF_UNICODE, + .expect_state = {[VK_CONTROL] = 0x80, [VK_LCONTROL] = 0x80}, .todo_state = {[VK_LCONTROL] = TRUE}, + .expect = {KEY_HOOK(WM_KEYDOWN, 0xc0, VK_LCONTROL, .todo_value = TRUE), KEY_MSG(WM_KEYDOWN, 0xc0, VK_CONTROL), {0}}}, + {.scan = 0x3c0, .vkey = VK_CONTROL, .flags = KEYEVENTF_UNICODE | KEYEVENTF_KEYUP, + .expect = {KEY_HOOK(WM_KEYUP, 0xc0, VK_LCONTROL, .todo_value = TRUE), KEY_MSG(WM_KEYUP, 0xc0, VK_CONTROL), {0}}}, + {0}, + }; + + struct send_input_keyboard_test unicode_vkey_packet[] = + { + {.scan = 0x3c0, .vkey = VK_PACKET, .flags = KEYEVENTF_UNICODE, .expect_state = {[VK_PACKET] = 0x80}, + .expect = {KEY_HOOK(WM_KEYDOWN, 0xc0, VK_PACKET), KEY_MSG(WM_KEYDOWN, 0, VK_PACKET, .todo_value = TRUE), WIN_MSG(WM_CHAR, 0xc0, 1), {0}}}, + {.scan = 0x3c0, .vkey = VK_PACKET, .flags = KEYEVENTF_UNICODE | KEYEVENTF_KEYUP, + .expect = {KEY_HOOK(WM_KEYUP, 0xc0, VK_PACKET), KEY_MSG(WM_KEYUP, 0, VK_PACKET, .todo_value = TRUE), {0}}}, + {0}, + }; + struct send_input_keyboard_test numpad_scan[] = { {.scan = 0x4b, .flags = KEYEVENTF_SCANCODE, .expect_state = {[VK_LEFT] = 0x80}, @@ -1288,6 +1307,8 @@ static void test_SendInput_keyboard_messages( WORD vkey, WORD scan, WCHAR wch, W check_send_input_keyboard_test( unicode, TRUE ); check_send_input_keyboard_test( lmenu_unicode_peeked, TRUE ); check_send_input_keyboard_test( unicode_vkey, TRUE ); + check_send_input_keyboard_test( unicode_vkey_ctrl, TRUE ); + check_send_input_keyboard_test( unicode_vkey_packet, TRUE ); check_send_input_keyboard_test( numpad_scan, TRUE ); check_send_input_keyboard_test( numpad_scan_numlock, TRUE ); winetest_pop_context(); @@ -1333,6 +1354,8 @@ static void test_SendInput_keyboard_messages( WORD vkey, WORD scan, WCHAR wch, W check_send_input_keyboard_test( unicode, FALSE ); check_send_input_keyboard_test( lmenu_unicode, FALSE ); check_send_input_keyboard_test( unicode_vkey, FALSE ); + check_send_input_keyboard_test( unicode_vkey_ctrl, FALSE ); + check_send_input_keyboard_test( unicode_vkey_packet, FALSE ); check_send_input_keyboard_test( numpad_scan, FALSE ); check_send_input_keyboard_test( numpad_scan_numlock, FALSE ); winetest_pop_context();
From: Alexandros Frantzis alexandros.frantzis@collabora.com
--- dlls/user32/tests/input.c | 161 +++++++++++++++++++++++++++++++++++++- 1 file changed, 160 insertions(+), 1 deletion(-)
diff --git a/dlls/user32/tests/input.c b/dlls/user32/tests/input.c index f21b9e24555..382e15b9ed2 100644 --- a/dlls/user32/tests/input.c +++ b/dlls/user32/tests/input.c @@ -111,6 +111,7 @@ enum user_function MSG_TEST_WIN = 1, LL_HOOK_KEYBD, LL_HOOK_MOUSE, + RAW_INPUT_KEYBOARD, };
struct user_call @@ -143,6 +144,12 @@ struct user_call UINT time; UINT_PTR extra; } ll_hook_ms; + struct + { + HWND hwnd; + BYTE code; + RAWKEYBOARD kbd; + } raw_input; };
BOOL todo; @@ -225,6 +232,15 @@ static int ok_call_( const char *file, int line, const struct user_call *expecte if (0 && (ret = expected->ll_hook_ms.time - received->ll_hook_ms.time)) goto done; if ((ret = (expected->ll_hook_ms.extra - received->ll_hook_ms.extra))) goto done; break; + case RAW_INPUT_KEYBOARD: + if ((ret = expected->raw_input.hwnd - received->raw_input.hwnd)) goto done; + if ((ret = expected->raw_input.code - received->raw_input.code)) goto done; + if ((ret = expected->raw_input.kbd.MakeCode - received->raw_input.kbd.MakeCode)) goto done; + if ((ret = expected->raw_input.kbd.Flags - received->raw_input.kbd.Flags)) goto done; + if ((ret = expected->raw_input.kbd.VKey - received->raw_input.kbd.VKey)) goto done; + if ((ret = expected->raw_input.kbd.Message - received->raw_input.kbd.Message)) goto done; + if ((ret = expected->raw_input.kbd.ExtraInformation - received->raw_input.kbd.ExtraInformation)) goto done; + break; }
done: @@ -249,6 +265,13 @@ done: wine_dbgstr_point(&received->ll_hook_ms.point), received->ll_hook_ms.data, received->ll_hook_ms.flags, received->ll_hook_ms.time, received->ll_hook_ms.extra ); return ret; + case RAW_INPUT_KEYBOARD: + todo_wine_if( expected->todo || expected->todo_value ) + ok_(file, line)( !ret, "got WM_INPUT key hwnd %p, code %d, make_code %#x, flags %#x, vkey %s, message %s, extra %#lx\n", + received->raw_input.hwnd, received->raw_input.code, received->raw_input.kbd.MakeCode, + received->raw_input.kbd.Flags, debugstr_vk(received->raw_input.kbd.VKey), + debugstr_wm(received->raw_input.kbd.Message), received->raw_input.kbd.ExtraInformation ); + return ret; }
switch (expected->func) @@ -270,6 +293,13 @@ done: wine_dbgstr_point(&received->ll_hook_ms.point), received->ll_hook_ms.data, received->ll_hook_ms.flags, received->ll_hook_ms.time, received->ll_hook_ms.extra ); return ret; + case RAW_INPUT_KEYBOARD: + todo_wine_if( expected->todo || expected->todo_value ) + ok_(file, line)( !ret, "got WM_INPUT key hwnd %p, code %d, make_code %#x, flags %#x, vkey %s, message %s, extra %#lx\n", + expected->raw_input.hwnd, expected->raw_input.code, expected->raw_input.kbd.MakeCode, + expected->raw_input.kbd.Flags, debugstr_vk(expected->raw_input.kbd.VKey), + debugstr_wm(expected->raw_input.kbd.Message), expected->raw_input.kbd.ExtraInformation ); + return ret; }
return 0; @@ -342,9 +372,32 @@ static void append_ll_hook_ms( UINT msg, const MSLLHOOKSTRUCT *info ) } }
+static void append_rawinput_message( HWND hwnd, WPARAM wparam, HRAWINPUT handle ) +{ + RAWINPUT rawinput; + UINT size = sizeof(rawinput), ret; + + ret = GetRawInputData( handle, RID_INPUT, &rawinput, &size, sizeof(RAWINPUTHEADER) ); + ok_ne( ret, (UINT)-1, UINT, "%u" ); + + if (rawinput.header.dwType == RIM_TYPEKEYBOARD) + { + struct user_call call = + { + .func = RAW_INPUT_KEYBOARD, + .raw_input = {.hwnd = hwnd, .code = GET_RAWINPUT_CODE_WPARAM(wparam), .kbd = rawinput.data.keyboard} + }; + ULONG index = InterlockedIncrement( ¤t_sequence_len ) - 1; + ok( index < ARRAY_SIZE(current_sequence), "got %lu calls\n", index ); + if (!append_message_hwnd) call.message.hwnd = 0; + current_sequence[index] = call; + } +} + static void append_message( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam ) { - if (!p_accept_message || p_accept_message( msg )) + if (msg == WM_INPUT) append_rawinput_message( hwnd, wparam, (HRAWINPUT)lparam ); + else if (!p_accept_message || p_accept_message( msg )) { struct user_call call = {.func = MSG_TEST_WIN, .message = {.hwnd = hwnd, .msg = msg, .wparam = wparam, .lparam = lparam}}; ULONG index = InterlockedIncrement( ¤t_sequence_len ) - 1; @@ -1381,6 +1434,111 @@ static void test_keynames(void) } }
+static BOOL accept_keyboard_messages_raw( UINT msg ) +{ + return is_keyboard_message( msg ) || msg == WM_INPUT; +} + +static void test_SendInput_raw_key_messages( WORD vkey, WORD wch, HKL hkl ) +{ +#define WIN_MSG(m, w, l, ...) {.func = MSG_TEST_WIN, .message = {.msg = m, .wparam = w, .lparam = l}, ## __VA_ARGS__} +#define RAW_KEY(s, f, v, m, ...) {.func = RAW_INPUT_KEYBOARD, .raw_input.kbd = {.MakeCode = s, .Flags = f, .VKey = v, .Message = m}, ## __VA_ARGS__} +#define KEY_MSG(m, s, v, ...) WIN_MSG( m, v, MAKELONG(1, (s) | (m == WM_KEYUP || m == WM_SYSKEYUP ? (KF_UP | KF_REPEAT) : 0)), ## __VA_ARGS__ ) + struct send_input_keyboard_test raw_legacy[] = + { + {.vkey = vkey, + .expect = {RAW_KEY(1, RI_KEY_MAKE, vkey, WM_KEYDOWN), KEY_MSG(WM_KEYDOWN, 1, vkey), WIN_MSG(WM_CHAR, wch, MAKELONG(1, 1)), {0}}}, + {.vkey = vkey, .flags = KEYEVENTF_KEYUP, + .expect = {RAW_KEY(2, RI_KEY_BREAK, vkey, WM_KEYUP), KEY_MSG(WM_KEYUP, 2, vkey), {0}}}, + {0}, + }; + struct send_input_keyboard_test raw_nolegacy[] = + { + {.vkey = vkey, .expect = {RAW_KEY(1, RI_KEY_MAKE, vkey, WM_KEYDOWN), {0}}}, + {.vkey = vkey, .flags = KEYEVENTF_KEYUP, .expect = {RAW_KEY(2, RI_KEY_BREAK, vkey, WM_KEYUP), {0}}}, + {0}, + }; + struct send_input_keyboard_test raw_vk_packet_legacy[] = + { + {.vkey = VK_PACKET, .expect_state = {[VK_PACKET] = 0x80}, + .expect = {RAW_KEY(1, RI_KEY_MAKE, VK_PACKET, WM_KEYDOWN), KEY_MSG(WM_KEYDOWN, 1, VK_PACKET), {0, .todo = TRUE}}}, + {.vkey = VK_PACKET, .flags = KEYEVENTF_KEYUP, + .expect = {RAW_KEY(2, RI_KEY_BREAK, VK_PACKET, WM_KEYUP), KEY_MSG(WM_KEYUP, 2, VK_PACKET), {0}}}, + {0}, + }; + struct send_input_keyboard_test raw_vk_packet_nolegacy[] = + { + {.vkey = VK_PACKET, .expect = {RAW_KEY(1, RI_KEY_MAKE, VK_PACKET, WM_KEYDOWN), {0}}}, + {.vkey = VK_PACKET, .flags = KEYEVENTF_KEYUP, .expect = {RAW_KEY(2, RI_KEY_BREAK, VK_PACKET, WM_KEYUP), {0}}}, + {0}, + }; +#undef WIN_MSG +#undef RAW_KEY +#undef KEY_MSG + RAWINPUTDEVICE rid = {.usUsagePage = HID_USAGE_PAGE_GENERIC, .usUsage = HID_USAGE_GENERIC_KEYBOARD}; + int receive; + HWND hwnd; + + raw_legacy[0].expect_state[vkey] = 0x80; + + hwnd = CreateWindowW( L"static", NULL, WS_POPUP | WS_VISIBLE, 0, 0, 100, 100, NULL, NULL, NULL, NULL ); + ok_ne( NULL, hwnd, HWND, "%p" ); + wait_messages( 100, FALSE ); + + /* If we have had a spurious layout change, wch may be incorrect. */ + if (GetKeyboardLayout( 0 ) != hkl) + { + win_skip( "Spurious keyboard layout changed detected (expected: %p got: %p)\n", + hkl, GetKeyboardLayout( 0 ) ); + ok_ret( 1, DestroyWindow( hwnd ) ); + wait_messages( 100, FALSE ); + ok_seq( empty_sequence ); + return; + } + + p_accept_message = accept_keyboard_messages_raw; + + for (receive = 0; receive <= 1; receive++) + { + winetest_push_context( receive ? "receive" : "peek" ); + + if (receive) + { + /* test received messages */ + LONG_PTR old_proc = SetWindowLongPtrW( hwnd, GWLP_WNDPROC, (LONG_PTR)append_message_wndproc ); + ok_ne( 0, old_proc, LONG_PTR, "%#Ix" ); + } + + rid.dwFlags = 0; + ok_ret( 1, RegisterRawInputDevices( &rid, 1, sizeof(rid) ) ); + + /* get both WM_INPUT and legacy messages */ + check_send_input_keyboard_test( raw_legacy, !receive ); + check_send_input_keyboard_test( raw_vk_packet_legacy, !receive ); + + rid.dwFlags = RIDEV_REMOVE; + ok_ret( 1, RegisterRawInputDevices( &rid, 1, sizeof(rid) ) ); + + rid.dwFlags = RIDEV_NOLEGACY; + ok_ret( 1, RegisterRawInputDevices( &rid, 1, sizeof(rid) ) ); + + /* get only WM_INPUT messages */ + check_send_input_keyboard_test( raw_nolegacy, !receive ); + check_send_input_keyboard_test( raw_vk_packet_nolegacy, !receive ); + + rid.dwFlags = RIDEV_REMOVE; + ok_ret( 1, RegisterRawInputDevices( &rid, 1, sizeof(rid) ) ); + + winetest_pop_context(); + } + + ok_ret( 1, DestroyWindow( hwnd ) ); + wait_messages( 100, FALSE ); + ok_seq( empty_sequence ); + + p_accept_message = NULL; +} + static void test_GetMouseMovePointsEx( char **argv ) { #define BUFLIM 64 @@ -5896,6 +6054,7 @@ static void test_input_desktop( char **argv ) test_RegisterRawInputDevices(); test_GetRawInputData(); test_GetRawInputBuffer(); + test_SendInput_raw_key_messages( 'F', wch, hkl );
test_LoadKeyboardLayoutEx( hkl );
From: Alexandros Frantzis alexandros.frantzis@collabora.com
--- dlls/user32/tests/input.c | 12 ++++++------ server/queue.c | 3 +++ 2 files changed, 9 insertions(+), 6 deletions(-)
diff --git a/dlls/user32/tests/input.c b/dlls/user32/tests/input.c index 382e15b9ed2..560baa399da 100644 --- a/dlls/user32/tests/input.c +++ b/dlls/user32/tests/input.c @@ -907,9 +907,9 @@ static void test_SendInput_keyboard_messages( WORD vkey, WORD scan, WCHAR wch, W static const struct send_input_keyboard_test shift[] = { {.vkey = VK_SHIFT, .expect_state = {[VK_SHIFT] = 0x80, [VK_LSHIFT] = 0x80}, - .expect = {KEY_HOOK(WM_KEYDOWN, 1, VK_LSHIFT, .todo_value = TRUE), KEY_MSG(WM_KEYDOWN, 1, VK_SHIFT), {0}}}, + .expect = {KEY_HOOK(WM_KEYDOWN, 1, VK_LSHIFT), KEY_MSG(WM_KEYDOWN, 1, VK_SHIFT), {0}}}, {.vkey = VK_SHIFT, .flags = KEYEVENTF_KEYUP, - .expect = {KEY_HOOK(WM_KEYUP, 2, VK_LSHIFT, .todo_value = TRUE), KEY_MSG(WM_KEYUP, 2, VK_SHIFT), {0}}}, + .expect = {KEY_HOOK(WM_KEYUP, 2, VK_LSHIFT), KEY_MSG(WM_KEYUP, 2, VK_SHIFT), {0}}}, {0}, }; static const struct send_input_keyboard_test shift_ext[] = @@ -948,17 +948,17 @@ static void test_SendInput_keyboard_messages( WORD vkey, WORD scan, WCHAR wch, W static const struct send_input_keyboard_test control[] = { {.vkey = VK_CONTROL, .expect_state = {[VK_CONTROL] = 0x80, [VK_LCONTROL] = 0x80}, - .expect = {KEY_HOOK(WM_KEYDOWN, 1, VK_LCONTROL, .todo_value = TRUE), KEY_MSG(WM_KEYDOWN, 1, VK_CONTROL), {0}}}, + .expect = {KEY_HOOK(WM_KEYDOWN, 1, VK_LCONTROL), KEY_MSG(WM_KEYDOWN, 1, VK_CONTROL), {0}}}, {.vkey = VK_CONTROL, .flags = KEYEVENTF_KEYUP, .expect_state = {0}, - .expect = {KEY_HOOK(WM_KEYUP, 2, VK_LCONTROL, .todo_value = TRUE), KEY_MSG(WM_KEYUP, 2, VK_CONTROL), {0}}}, + .expect = {KEY_HOOK(WM_KEYUP, 2, VK_LCONTROL), KEY_MSG(WM_KEYUP, 2, VK_CONTROL), {0}}}, {0}, }; static const struct send_input_keyboard_test control_ext[] = { {.vkey = VK_CONTROL, .flags = KEYEVENTF_EXTENDEDKEY, .expect_state = {[VK_CONTROL] = 0x80, [VK_RCONTROL] = 0x80}, - .expect = {KEY_HOOK_(WM_KEYDOWN, 1, VK_RCONTROL, LLKHF_EXTENDED, .todo_value = TRUE), KEY_MSG_(WM_KEYDOWN, 1, VK_CONTROL, KF_EXTENDED), {0}}}, + .expect = {KEY_HOOK_(WM_KEYDOWN, 1, VK_RCONTROL, LLKHF_EXTENDED), KEY_MSG_(WM_KEYDOWN, 1, VK_CONTROL, KF_EXTENDED), {0}}}, {.vkey = VK_CONTROL, .flags = KEYEVENTF_KEYUP | KEYEVENTF_EXTENDEDKEY, - .expect = {KEY_HOOK_(WM_KEYUP, 2, VK_RCONTROL, LLKHF_EXTENDED, .todo_value = TRUE), KEY_MSG_(WM_KEYUP, 2, VK_CONTROL, KF_EXTENDED), {0}}}, + .expect = {KEY_HOOK_(WM_KEYUP, 2, VK_RCONTROL, LLKHF_EXTENDED), KEY_MSG_(WM_KEYUP, 2, VK_CONTROL, KF_EXTENDED), {0}}}, {0}, };
diff --git a/server/queue.c b/server/queue.c index 6d4396234f2..3ffa48c3928 100644 --- a/server/queue.c +++ b/server/queue.c @@ -2111,16 +2111,19 @@ static int queue_keyboard_message( struct desktop *desktop, user_handle_t win, c case VK_LMENU: case VK_RMENU: vkey = (input->kbd.flags & KEYEVENTF_EXTENDEDKEY) ? VK_RMENU : VK_LMENU; + if ((input->kbd.vkey & 0xff) == VK_MENU) hook_vkey = vkey; break; case VK_CONTROL: case VK_LCONTROL: case VK_RCONTROL: vkey = (input->kbd.flags & KEYEVENTF_EXTENDEDKEY) ? VK_RCONTROL : VK_LCONTROL; + if ((input->kbd.vkey & 0xff) == VK_CONTROL) hook_vkey = vkey; break; case VK_SHIFT: case VK_LSHIFT: case VK_RSHIFT: vkey = (input->kbd.flags & KEYEVENTF_EXTENDEDKEY) ? VK_RSHIFT : VK_LSHIFT; + if ((input->kbd.vkey & 0xff) == VK_SHIFT) hook_vkey = vkey; break; } }
From: Alexandros Frantzis alexandros.frantzis@collabora.com
--- dlls/user32/tests/input.c | 7 +++---- server/queue.c | 41 ++++++++++++++++++--------------------- 2 files changed, 22 insertions(+), 26 deletions(-)
diff --git a/dlls/user32/tests/input.c b/dlls/user32/tests/input.c index 560baa399da..0bcea006db1 100644 --- a/dlls/user32/tests/input.c +++ b/dlls/user32/tests/input.c @@ -1224,11 +1224,10 @@ static void test_SendInput_keyboard_messages( WORD vkey, WORD scan, WCHAR wch, W
struct send_input_keyboard_test unicode_vkey_ctrl[] = { - {.scan = 0x3c0, .vkey = VK_CONTROL, .flags = KEYEVENTF_UNICODE, - .expect_state = {[VK_CONTROL] = 0x80, [VK_LCONTROL] = 0x80}, .todo_state = {[VK_LCONTROL] = TRUE}, - .expect = {KEY_HOOK(WM_KEYDOWN, 0xc0, VK_LCONTROL, .todo_value = TRUE), KEY_MSG(WM_KEYDOWN, 0xc0, VK_CONTROL), {0}}}, + {.scan = 0x3c0, .vkey = VK_CONTROL, .flags = KEYEVENTF_UNICODE, .expect_state = {[VK_CONTROL] = 0x80, [VK_LCONTROL] = 0x80}, + .expect = {KEY_HOOK(WM_KEYDOWN, 0xc0, VK_LCONTROL), KEY_MSG(WM_KEYDOWN, 0xc0, VK_CONTROL), {0}}}, {.scan = 0x3c0, .vkey = VK_CONTROL, .flags = KEYEVENTF_UNICODE | KEYEVENTF_KEYUP, - .expect = {KEY_HOOK(WM_KEYUP, 0xc0, VK_LCONTROL, .todo_value = TRUE), KEY_MSG(WM_KEYUP, 0xc0, VK_CONTROL), {0}}}, + .expect = {KEY_HOOK(WM_KEYUP, 0xc0, VK_LCONTROL), KEY_MSG(WM_KEYUP, 0xc0, VK_CONTROL), {0}}}, {0}, };
diff --git a/server/queue.c b/server/queue.c index 3ffa48c3928..7d1cf780837 100644 --- a/server/queue.c +++ b/server/queue.c @@ -2103,29 +2103,26 @@ static int queue_keyboard_message( struct desktop *desktop, user_handle_t win, c
if (!(time = input->kbd.time)) time = get_tick_count();
- if (!(input->kbd.flags & KEYEVENTF_UNICODE)) + switch (vkey) { - switch (vkey) - { - case VK_MENU: - case VK_LMENU: - case VK_RMENU: - vkey = (input->kbd.flags & KEYEVENTF_EXTENDEDKEY) ? VK_RMENU : VK_LMENU; - if ((input->kbd.vkey & 0xff) == VK_MENU) hook_vkey = vkey; - break; - case VK_CONTROL: - case VK_LCONTROL: - case VK_RCONTROL: - vkey = (input->kbd.flags & KEYEVENTF_EXTENDEDKEY) ? VK_RCONTROL : VK_LCONTROL; - if ((input->kbd.vkey & 0xff) == VK_CONTROL) hook_vkey = vkey; - break; - case VK_SHIFT: - case VK_LSHIFT: - case VK_RSHIFT: - vkey = (input->kbd.flags & KEYEVENTF_EXTENDEDKEY) ? VK_RSHIFT : VK_LSHIFT; - if ((input->kbd.vkey & 0xff) == VK_SHIFT) hook_vkey = vkey; - break; - } + case VK_MENU: + case VK_LMENU: + case VK_RMENU: + vkey = (input->kbd.flags & KEYEVENTF_EXTENDEDKEY) ? VK_RMENU : VK_LMENU; + if ((input->kbd.vkey & 0xff) == VK_MENU) hook_vkey = vkey; + break; + case VK_CONTROL: + case VK_LCONTROL: + case VK_RCONTROL: + vkey = (input->kbd.flags & KEYEVENTF_EXTENDEDKEY) ? VK_RCONTROL : VK_LCONTROL; + if ((input->kbd.vkey & 0xff) == VK_CONTROL) hook_vkey = vkey; + break; + case VK_SHIFT: + case VK_LSHIFT: + case VK_RSHIFT: + vkey = (input->kbd.flags & KEYEVENTF_EXTENDEDKEY) ? VK_RSHIFT : VK_LSHIFT; + if ((input->kbd.vkey & 0xff) == VK_SHIFT) hook_vkey = vkey; + break; }
message_code = (input->kbd.flags & KEYEVENTF_KEYUP) ? WM_KEYUP : WM_KEYDOWN;
From: Alexandros Frantzis alexandros.frantzis@collabora.com
--- dlls/user32/tests/input.c | 35 +++++++++++++++++++++++++++++++++++ server/queue.c | 5 +++-- 2 files changed, 38 insertions(+), 2 deletions(-)
diff --git a/dlls/user32/tests/input.c b/dlls/user32/tests/input.c index 0bcea006db1..f4d3b182ca7 100644 --- a/dlls/user32/tests/input.c +++ b/dlls/user32/tests/input.c @@ -1471,6 +1471,35 @@ static void test_SendInput_raw_key_messages( WORD vkey, WORD wch, HKL hkl ) {.vkey = VK_PACKET, .flags = KEYEVENTF_KEYUP, .expect = {RAW_KEY(2, RI_KEY_BREAK, VK_PACKET, WM_KEYUP), {0}}}, {0}, }; + struct send_input_keyboard_test raw_unicode_legacy[] = + { + {.scan = 0x3c0, .flags = KEYEVENTF_UNICODE, .expect_state = {[VK_PACKET] = 0x80}, + .expect = {KEY_MSG(WM_KEYDOWN, 0, VK_PACKET, .todo_value = TRUE), WIN_MSG(WM_CHAR, 0x3c0, 1), {0}}}, + {.scan = 0x3c0, .flags = KEYEVENTF_KEYUP | KEYEVENTF_UNICODE, + .expect = {KEY_MSG(WM_KEYUP, 0, VK_PACKET, .todo_value = TRUE), {0}}}, + {0}, + }; + struct send_input_keyboard_test raw_unicode_nolegacy[] = + { + {.scan = 0x3c0, .flags = KEYEVENTF_UNICODE}, + {.scan = 0x3c0, .flags = KEYEVENTF_KEYUP | KEYEVENTF_UNICODE}, + {0}, + }; + struct send_input_keyboard_test raw_unicode_vkey_ctrl_legacy[] = + { + {.scan = 0x3c0, .vkey = VK_CONTROL, .flags = KEYEVENTF_UNICODE, + .expect_state = {[VK_CONTROL] = 0x80, [VK_LCONTROL] = 0x80}, + .expect = {KEY_MSG(WM_KEYDOWN, 0xc0, VK_CONTROL), {0}}}, + {.scan = 0x3c0, .vkey = VK_CONTROL, .flags = KEYEVENTF_UNICODE | KEYEVENTF_KEYUP, + .expect = {KEY_MSG(WM_KEYUP, 0xc0, VK_CONTROL), {0}}}, + {0}, + }; + struct send_input_keyboard_test raw_unicode_vkey_ctrl_nolegacy[] = + { + {.scan = 0x3c0, .vkey = VK_CONTROL, .flags = KEYEVENTF_UNICODE}, + {.scan = 0x3c0, .vkey = VK_CONTROL, .flags = KEYEVENTF_UNICODE | KEYEVENTF_KEYUP}, + {0}, + }; #undef WIN_MSG #undef RAW_KEY #undef KEY_MSG @@ -1514,6 +1543,9 @@ static void test_SendInput_raw_key_messages( WORD vkey, WORD wch, HKL hkl ) /* get both WM_INPUT and legacy messages */ check_send_input_keyboard_test( raw_legacy, !receive ); check_send_input_keyboard_test( raw_vk_packet_legacy, !receive ); + /* no WM_INPUT message for unicode */ + check_send_input_keyboard_test( raw_unicode_legacy, !receive ); + check_send_input_keyboard_test( raw_unicode_vkey_ctrl_legacy, !receive );
rid.dwFlags = RIDEV_REMOVE; ok_ret( 1, RegisterRawInputDevices( &rid, 1, sizeof(rid) ) ); @@ -1524,6 +1556,9 @@ static void test_SendInput_raw_key_messages( WORD vkey, WORD wch, HKL hkl ) /* get only WM_INPUT messages */ check_send_input_keyboard_test( raw_nolegacy, !receive ); check_send_input_keyboard_test( raw_vk_packet_nolegacy, !receive ); + /* no WM_INPUT message for unicode */ + check_send_input_keyboard_test( raw_unicode_nolegacy, !receive ); + check_send_input_keyboard_test( raw_unicode_vkey_ctrl_nolegacy, !receive );
rid.dwFlags = RIDEV_REMOVE; ok_ret( 1, RegisterRawInputDevices( &rid, 1, sizeof(rid) ) ); diff --git a/server/queue.c b/server/queue.c index 7d1cf780837..4d48da1d484 100644 --- a/server/queue.c +++ b/server/queue.c @@ -2099,6 +2099,7 @@ static int queue_keyboard_message( struct desktop *desktop, user_handle_t win, c unsigned int message_code, time; lparam_t lparam = input->kbd.scan << 16; unsigned int flags = 0; + BOOL unicode = input->kbd.flags & KEYEVENTF_UNICODE; int wait;
if (!(time = input->kbd.time)) time = get_tick_count(); @@ -2188,7 +2189,7 @@ static int queue_keyboard_message( struct desktop *desktop, user_handle_t win, c } }
- if ((foreground = get_foreground_thread( desktop, win ))) + if (!unicode && (foreground = get_foreground_thread( desktop, win ))) { struct rawinput_message raw_msg = {0}; raw_msg.foreground = foreground; @@ -2216,7 +2217,7 @@ static int queue_keyboard_message( struct desktop *desktop, user_handle_t win, c msg->msg = message_code; if (origin == IMO_INJECTED) msg_data->flags = LLKHF_INJECTED;
- if (input->kbd.flags & KEYEVENTF_UNICODE && !vkey) + if (unicode && !vkey) { vkey = hook_vkey = VK_PACKET; }
From: Alexandros Frantzis alexandros.frantzis@collabora.com
--- dlls/user32/tests/input.c | 32 ++++++++++++++++++++++++++++---- 1 file changed, 28 insertions(+), 4 deletions(-)
diff --git a/dlls/user32/tests/input.c b/dlls/user32/tests/input.c index f4d3b182ca7..94db58e6796 100644 --- a/dlls/user32/tests/input.c +++ b/dlls/user32/tests/input.c @@ -628,6 +628,9 @@ struct send_input_keyboard_test struct user_call expect[8]; BYTE expect_state[256]; BOOL todo_state[256]; + BOOL async; + BYTE expect_async[256]; + BOOL todo_async[256]; };
static LRESULT CALLBACK ll_hook_kbd_proc(int code, WPARAM wparam, LPARAM lparam) @@ -668,6 +671,22 @@ static void check_keyboard_state_( int line, const BYTE expect_state[256], const } }
+#define check_keyboard_async( a, b ) check_keyboard_async_( __LINE__, a, b ) +static void check_keyboard_async_( int line, const BYTE expect_state[256], const BOOL todo_state[256] ) +{ + UINT i; + + /* TODO: figure out if the async state for vkey 0 provides any information and + * add it to the check. */ + for (i = 1; i < 256; i++) + { + BYTE state = GetAsyncKeyState(i) >> 8; + todo_wine_if( todo_state[i] ) + ok_(__FILE__, line)( (expect_state[i] & 0x80) == (state & 0x80), + "async got %s: %#x\n", debugstr_vk( i ), state ); + } +} + static void clear_keyboard_state( void ) { static BYTE empty_state[256] = {0}; @@ -713,6 +732,7 @@ static void check_send_input_keyboard_test_( const struct send_input_keyboard_te
ok_seq( test->expect ); check_keyboard_state( test->expect_state, test->todo_state ); + if (test->async) check_keyboard_async( test->expect_async, test->todo_async );
winetest_pop_context(); } @@ -1453,7 +1473,7 @@ static void test_SendInput_raw_key_messages( WORD vkey, WORD wch, HKL hkl ) }; struct send_input_keyboard_test raw_nolegacy[] = { - {.vkey = vkey, .expect = {RAW_KEY(1, RI_KEY_MAKE, vkey, WM_KEYDOWN), {0}}}, + {.vkey = vkey, .async = TRUE, .expect = {RAW_KEY(1, RI_KEY_MAKE, vkey, WM_KEYDOWN), {0}}}, {.vkey = vkey, .flags = KEYEVENTF_KEYUP, .expect = {RAW_KEY(2, RI_KEY_BREAK, vkey, WM_KEYUP), {0}}}, {0}, }; @@ -1467,7 +1487,8 @@ static void test_SendInput_raw_key_messages( WORD vkey, WORD wch, HKL hkl ) }; struct send_input_keyboard_test raw_vk_packet_nolegacy[] = { - {.vkey = VK_PACKET, .expect = {RAW_KEY(1, RI_KEY_MAKE, VK_PACKET, WM_KEYDOWN), {0}}}, + {.vkey = VK_PACKET, .async = TRUE, .expect_async = {[VK_PACKET] = 0x80}, + .expect = {RAW_KEY(1, RI_KEY_MAKE, VK_PACKET, WM_KEYDOWN), {0}}}, {.vkey = VK_PACKET, .flags = KEYEVENTF_KEYUP, .expect = {RAW_KEY(2, RI_KEY_BREAK, VK_PACKET, WM_KEYUP), {0}}}, {0}, }; @@ -1481,7 +1502,8 @@ static void test_SendInput_raw_key_messages( WORD vkey, WORD wch, HKL hkl ) }; struct send_input_keyboard_test raw_unicode_nolegacy[] = { - {.scan = 0x3c0, .flags = KEYEVENTF_UNICODE}, + {.scan = 0x3c0, .flags = KEYEVENTF_UNICODE, .async = TRUE, + .expect_async = {[VK_PACKET] = 0x80}, .todo_async = {[VK_PACKET] = TRUE}}, {.scan = 0x3c0, .flags = KEYEVENTF_KEYUP | KEYEVENTF_UNICODE}, {0}, }; @@ -1496,7 +1518,8 @@ static void test_SendInput_raw_key_messages( WORD vkey, WORD wch, HKL hkl ) }; struct send_input_keyboard_test raw_unicode_vkey_ctrl_nolegacy[] = { - {.scan = 0x3c0, .vkey = VK_CONTROL, .flags = KEYEVENTF_UNICODE}, + {.scan = 0x3c0, .vkey = VK_CONTROL, .flags = KEYEVENTF_UNICODE, .async = TRUE, + .expect_async = {[VK_CONTROL] = 0x80, [VK_LCONTROL] = 0x80}}, {.scan = 0x3c0, .vkey = VK_CONTROL, .flags = KEYEVENTF_UNICODE | KEYEVENTF_KEYUP}, {0}, }; @@ -1508,6 +1531,7 @@ static void test_SendInput_raw_key_messages( WORD vkey, WORD wch, HKL hkl ) HWND hwnd;
raw_legacy[0].expect_state[vkey] = 0x80; + raw_nolegacy[0].expect_async[vkey] = 0x80;
hwnd = CreateWindowW( L"static", NULL, WS_POPUP | WS_VISIBLE, 0, 0, 100, 100, NULL, NULL, NULL, NULL ); ok_ne( NULL, hwnd, HWND, "%p" );
From: Alexandros Frantzis alexandros.frantzis@collabora.com
--- dlls/user32/tests/input.c | 3 +-- server/queue.c | 8 +++----- 2 files changed, 4 insertions(+), 7 deletions(-)
diff --git a/dlls/user32/tests/input.c b/dlls/user32/tests/input.c index 94db58e6796..9196e67c15b 100644 --- a/dlls/user32/tests/input.c +++ b/dlls/user32/tests/input.c @@ -1502,8 +1502,7 @@ static void test_SendInput_raw_key_messages( WORD vkey, WORD wch, HKL hkl ) }; struct send_input_keyboard_test raw_unicode_nolegacy[] = { - {.scan = 0x3c0, .flags = KEYEVENTF_UNICODE, .async = TRUE, - .expect_async = {[VK_PACKET] = 0x80}, .todo_async = {[VK_PACKET] = TRUE}}, + {.scan = 0x3c0, .flags = KEYEVENTF_UNICODE, .async = TRUE, .expect_async = {[VK_PACKET] = 0x80}}, {.scan = 0x3c0, .flags = KEYEVENTF_KEYUP | KEYEVENTF_UNICODE}, {0}, }; diff --git a/server/queue.c b/server/queue.c index 4d48da1d484..50f68e39f6f 100644 --- a/server/queue.c +++ b/server/queue.c @@ -2189,6 +2189,8 @@ static int queue_keyboard_message( struct desktop *desktop, user_handle_t win, c } }
+ if (unicode && !vkey) vkey = hook_vkey = VK_PACKET; + if (!unicode && (foreground = get_foreground_thread( desktop, win ))) { struct rawinput_message raw_msg = {0}; @@ -2217,11 +2219,7 @@ static int queue_keyboard_message( struct desktop *desktop, user_handle_t win, c msg->msg = message_code; if (origin == IMO_INJECTED) msg_data->flags = LLKHF_INJECTED;
- if (unicode && !vkey) - { - vkey = hook_vkey = VK_PACKET; - } - else + if (!unicode || input->kbd.vkey) { if (input->kbd.flags & KEYEVENTF_EXTENDEDKEY) flags |= KF_EXTENDED; /* FIXME: set KF_DLGMODE and KF_MENUMODE when needed */
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=145975
Your paranoid android.
=== w11pro64 (32 bit report) ===
user32: input.c:4417: Test failed: layered 0: button_up_hwnd_todo 0: got MSG_TEST_WIN hwnd 00050040, msg WM_LBUTTONUP, wparam 0, lparam 0x320032 input.c:4417: Test failed: layered 0: button_up_hwnd_todo 1 (missing): MSG_TEST_WIN hwnd 00050040, WM_LBUTTONUP, wparam 0, lparam 0x320032
=== w11pro64_amd (64 bit report) ===
user32: input.c:4283: Test failed: button_up_no_message 0 (missing): LL_HOOK_MOUSE msg 0, point (0,0), data 0, flags 0, time 0, extra 0 input.c:4305: Test failed: button_down_hwnd_todo 1 (missing): MSG_TEST_WIN hwnd 000000000007007E, WM_LBUTTONDOWN, wparam 0x1, lparam 0x320032 input.c:4309: Test failed: button_up_hwnd 1: got MSG_TEST_WIN hwnd 000000000007007E, msg WM_LBUTTONDOWN, wparam 0x1, lparam 0x320032 input.c:4309: Test failed: button_up_hwnd 2 (spurious): got MSG_TEST_WIN hwnd 000000000007007E, msg WM_LBUTTONUP, wparam 0, lparam 0x320032
On Fri May 31 10:11:26 2024 +0000, Alexandros Frantzis wrote:
changed this line in [version 2 of the diff](/wine/wine/-/merge_requests/5754/diffs?diff_id=116006&start_sha=1876fb016e667185e7dca345a9a60691bb4e1c4d#14b74bf2d7f44f52a31e4ee1baa51371f14df769_2107_2107)
I investigated/tested a bit more and removed the check completely in v2 (see new commit and tests). That is, my current conclusion is that the modifier vkey transformation happens independently of whether the unicode flag is set (could I be missing some more tests invalidate this conclusion?).
On Thu May 30 09:54:10 2024 +0000, Rémi Bernon wrote:
We don't use these struct literals anywhere in Wine, it's not too bad but I'm not sure I want to create a precedent here. As we only receive WM_INPUT when registering for it, what about doing something like:
static void append_rawinput_message( HWND hwnd, WPARAM wparam, HRAWINPUT handle ) { RAWINPUT rawinput; UINT size = sizeof(rawinput), ret; ret = GetRawInputData( handle, RID_INPUT, &rawinput, &size, sizeof(RAWINPUTHEADER) ); ok_ne( ret, (UINT)-1, UINT, "%u" ); if (rawinput.header.dwType == RIM_TYPEKEYBOARD) { struct user_call call = { .func = RAW_INPUT_KEYBOARD, .raw_input = {.hwnd = hwnd, .code = GET_RAWINPUT_CODE_WPARAM(wparam), .kbd = rawinput.data.keyboard} }; ULONG index = InterlockedIncrement( ¤t_sequence_len ) - 1; ok( index < ARRAY_SIZE(current_sequence), "got %lu calls\n", index ); if (!append_message_hwnd) call.message.hwnd = 0; current_sequence[index] = call; } } static void append_message( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam ) { if (msg == WM_INPUT) append_rawinput_message( hwnd, wparam, (HRAWINPUT)lparam ); else if (!p_accept_message || p_accept_message( msg )) { struct user_call call = {.func = MSG_TEST_WIN, .message = {.hwnd = hwnd, .msg = msg, .wparam = wparam, .lparam = lparam}}; ULONG index = InterlockedIncrement( ¤t_sequence_len ) - 1; ok( index < ARRAY_SIZE(current_sequence), "got %lu calls\n", index ); if (!append_message_hwnd) call.message.hwnd = 0; current_sequence[index] = call; } }
Done in v2.
On Fri May 31 10:11:28 2024 +0000, Alexandros Frantzis wrote:
changed this line in [version 2 of the diff](/wine/wine/-/merge_requests/5754/diffs?diff_id=116006&start_sha=1876fb016e667185e7dca345a9a60691bb4e1c4d#b4e51a57fda93554e5c008a209f92ef67bd4d777_1479_1528)
Done in v2.
On Fri May 31 10:11:29 2024 +0000, Alexandros Frantzis wrote:
changed this line in [version 2 of the diff](/wine/wine/-/merge_requests/5754/diffs?diff_id=116006&start_sha=1876fb016e667185e7dca345a9a60691bb4e1c4d#14b74bf2d7f44f52a31e4ee1baa51371f14df769_2209_2211)
Thanks for the info, I have reverted this change in v2. I also added optional async checks in the tests (we might want to iterate on the exact design), to gain some confidence that the change in the last commit in v2 is correct, but also to avoid inadvertent regressions like the one you mentioned.
@rbernon In v2 I have expanded the scope of the MR a bit, because I found the changes interacted with a few existing issues.
v2: * More tests to avoid regressions. * Added infrastructure for async key state checking and used it in raw input nolegacy tests. * Fixed pre-existing todos for modifier vkeys used in hooks.
Rémi Bernon (@rbernon) commented about server/queue.c:
} }
- if (unicode && !vkey) vkey = hook_vkey = VK_PACKET;
Nit: could this fit in the vkey fixup switch above, like this?
``` case 0: if (unicode) vkey = hook_vkey = VK_PACKET; break; ```
This merge request was approved by Rémi Bernon.
Thanks, looks great otherwise :)