This MR improves the handling of numpad keys for drivers using KBDTABLES (only the Wayland driver at this point). It achieves this by: 1. Allowing drivers to send only the scancode in keyboard events, with win32u performing the scan->vkey mapping internally. A nice side effect of this change is that it fixes a few user32 input test TODOs. 2. Enhancing wineserver to read extended KBD vkey attributes and perform numpad key mapping depending on modifier state. 3. Providing default VK_NUMPAD* -> WCHAR mappings in win32u.
-- v6: winewayland.drv: Populate vkey to wchar entry for VK_DECIMAL. user32/tests: Add tests for SendInput with numpad scancodes.
From: Alexandros Frantzis alexandros.frantzis@collabora.com
--- dlls/win32u/input.c | 1 + 1 file changed, 1 insertion(+)
diff --git a/dlls/win32u/input.c b/dlls/win32u/input.c index 04532e7d015..38350861c6b 100644 --- a/dlls/win32u/input.c +++ b/dlls/win32u/input.c @@ -521,6 +521,7 @@ static WCHAR kbd_tables_vkey_to_wchar( const KBDTABLES *tables, UINT vkey, const if (vkey >= 'A' && vkey <= 'Z') return vkey - 'A' + 1; tables = &kbdus_tables; } + if (vkey >= VK_NUMPAD0 && vkey <= VK_NUMPAD9) tables = &kbdus_tables;
mod = caps_mod = kbd_tables_get_mod_num( tables, state, FALSE ); if (caps) caps_mod = kbd_tables_get_mod_num( tables, state, TRUE );
From: Alexandros Frantzis alexandros.frantzis@collabora.com
The implementation will map the scan code to a virtual key code internally. --- dlls/user32/tests/input.c | 31 ++++++++++++++++++------- dlls/win32u/message.c | 23 +++++++++++++++--- dlls/winewayland.drv/wayland_keyboard.c | 9 ++++--- 3 files changed, 47 insertions(+), 16 deletions(-)
diff --git a/dlls/user32/tests/input.c b/dlls/user32/tests/input.c index d84d543bed8..889c24b1dcc 100644 --- a/dlls/user32/tests/input.c +++ b/dlls/user32/tests/input.c @@ -1063,16 +1063,26 @@ static void test_SendInput_keyboard_messages( WORD vkey, WORD scan, WCHAR wch, W struct send_input_keyboard_test rshift_scan[] = { {.scan = 0x36, .flags = KEYEVENTF_SCANCODE, .expect_state = {[VK_SHIFT] = 0x80, [VK_LSHIFT] = 0x80}, - .todo_state = {[0] = TRUE, [VK_SHIFT] = TRUE, [VK_LSHIFT] = TRUE}, - .expect = {KEY_HOOK(WM_KEYDOWN, 0x36, VK_RSHIFT, .todo_value = TRUE), KEY_MSG(WM_KEYDOWN, 0x36, VK_SHIFT, .todo_value = TRUE), {0}}}, + .expect = {KEY_HOOK(WM_KEYDOWN, 0x36, VK_RSHIFT), KEY_MSG(WM_KEYDOWN, 0x36, VK_SHIFT), {0}}}, {.scan = scan, .flags = KEYEVENTF_SCANCODE, .expect_state = {[VK_SHIFT] = 0x80, [VK_LSHIFT] = 0x80, /*[vkey] = 0x80*/}, - .todo_state = {[0] = TRUE, [VK_SHIFT] = TRUE, [VK_LSHIFT] = TRUE, /*[vkey] = TRUE*/}, - .expect = {KEY_HOOK(WM_KEYDOWN, scan, vkey, .todo_value = TRUE), KEY_MSG(WM_KEYDOWN, scan, vkey, .todo_value = TRUE), WIN_MSG(WM_CHAR, wch_shift, MAKELONG(1, scan), .todo = TRUE), {0}}}, + .expect = {KEY_HOOK(WM_KEYDOWN, scan, vkey), KEY_MSG(WM_KEYDOWN, scan, vkey), WIN_MSG(WM_CHAR, wch_shift, MAKELONG(1, scan)), {0}}}, {.scan = scan, .flags = KEYEVENTF_SCANCODE | KEYEVENTF_KEYUP, .expect_state = {[VK_SHIFT] = 0x80, [VK_LSHIFT] = 0x80}, - .todo_state = {[VK_SHIFT] = TRUE, [VK_LSHIFT] = TRUE}, - .expect = {KEY_HOOK(WM_KEYUP, scan, vkey, .todo_value = TRUE), KEY_MSG(WM_KEYUP, scan, vkey, .todo_value = TRUE), {0}}}, + .expect = {KEY_HOOK(WM_KEYUP, scan, vkey), KEY_MSG(WM_KEYUP, scan, vkey), {0}}}, {.scan = 0x36, .flags = KEYEVENTF_SCANCODE | KEYEVENTF_KEYUP, - .expect = {KEY_HOOK(WM_KEYUP, 0x36, VK_RSHIFT, .todo_value = TRUE), KEY_MSG(WM_KEYUP, 0x36, VK_SHIFT, .todo_value = TRUE), {0}}}, + .expect = {KEY_HOOK(WM_KEYUP, 0x36, VK_RSHIFT), KEY_MSG(WM_KEYUP, 0x36, VK_SHIFT), {0}}}, + {0}, + }; + + struct send_input_keyboard_test rctrl_scan[] = + { + {.scan = 0xe01d, .flags = KEYEVENTF_SCANCODE, .expect_state = {[VK_CONTROL] = 0x80, [VK_LCONTROL] = 0x80}, + .expect = {KEY_HOOK(WM_KEYDOWN, 0x1d, VK_LCONTROL), KEY_MSG(WM_KEYDOWN, 0x1d, VK_CONTROL), {0}}}, + {.scan = 0xe01d, .flags = KEYEVENTF_SCANCODE | KEYEVENTF_KEYUP, + .expect = {KEY_HOOK(WM_KEYUP, 0x1d, VK_LCONTROL), KEY_MSG(WM_KEYUP, 0x1d, VK_CONTROL), {0}}}, + {.scan = 0x1d, .flags = KEYEVENTF_SCANCODE | KEYEVENTF_EXTENDEDKEY, .expect_state = {[VK_CONTROL] = 0x80, [VK_RCONTROL] = 0x80}, + .expect = {KEY_HOOK_(WM_KEYDOWN, 0x1d, VK_RCONTROL, LLKHF_EXTENDED, .todo_value = TRUE), KEY_MSG(WM_KEYDOWN, 0x11d, VK_CONTROL), {0}}}, + {.scan = 0x1d, .flags = KEYEVENTF_SCANCODE | KEYEVENTF_EXTENDEDKEY | KEYEVENTF_KEYUP, + .expect = {KEY_HOOK_(WM_KEYUP, 0x1d, VK_RCONTROL, LLKHF_EXTENDED, .todo_value = TRUE), KEY_MSG(WM_KEYUP, 0x11d, VK_CONTROL), {0}}}, {0}, };
@@ -1179,7 +1189,6 @@ static void test_SendInput_keyboard_messages( WORD vkey, WORD scan, WCHAR wch, W lmenu_lcontrol_vkey[2].expect_state[vkey] = 0x80; shift_vkey[1].expect_state[vkey] = 0x80; rshift_scan[1].expect_state[vkey] = 0x80; - rshift_scan[1].todo_state[vkey] = TRUE; unicode_vkey[0].expect_state[vkey] = 0x80;
/* test peeked messages */ @@ -1211,6 +1220,9 @@ static void test_SendInput_keyboard_messages( WORD vkey, WORD scan, WCHAR wch, W else check_send_input_keyboard_test( menu_ext_peeked, TRUE ); check_send_input_keyboard_test( lrshift_ext, TRUE ); check_send_input_keyboard_test( rshift_scan, TRUE ); + /* Skip on Korean layouts since they map the right control key to VK_HANJA */ + if (LOWORD(hkl) == 0x0412) skip( "skipping rctrl_scan test on Korean layout" ); + else check_send_input_keyboard_test( rctrl_scan, TRUE ); check_send_input_keyboard_test( unicode, TRUE ); check_send_input_keyboard_test( lmenu_unicode_peeked, TRUE ); check_send_input_keyboard_test( unicode_vkey, TRUE ); @@ -1251,6 +1263,9 @@ static void test_SendInput_keyboard_messages( WORD vkey, WORD scan, WCHAR wch, W else check_send_input_keyboard_test( menu_ext, FALSE ); check_send_input_keyboard_test( lrshift_ext, FALSE ); check_send_input_keyboard_test( rshift_scan, FALSE ); + /* Skip on Korean layouts since they map the right control key to VK_HANJA */ + if (LOWORD(hkl) == 0x0412) skip( "skipping rctrl_scan test on Korean layout" ); + else check_send_input_keyboard_test( rctrl_scan, FALSE ); check_send_input_keyboard_test( unicode, FALSE ); check_send_input_keyboard_test( lmenu_unicode, FALSE ); check_send_input_keyboard_test( unicode_vkey, FALSE ); diff --git a/dlls/win32u/message.c b/dlls/win32u/message.c index 4a368ffb589..ddc9658f087 100644 --- a/dlls/win32u/message.c +++ b/dlls/win32u/message.c @@ -3558,9 +3558,26 @@ NTSTATUS send_hardware_message( HWND hwnd, UINT flags, const INPUT *input, LPARA MOUSEEVENTF_XDOWN | MOUSEEVENTF_XUP)); break; case INPUT_KEYBOARD: - req->input.kbd.vkey = input->ki.wVk; - req->input.kbd.scan = input->ki.wScan; - req->input.kbd.flags = input->ki.dwFlags; + if (input->ki.dwFlags & KEYEVENTF_SCANCODE) + { + UINT scan = input->ki.wScan; + /* TODO: Use the keyboard layout of the target hwnd, once + * NtUserGetKeyboardLayout supports non-current threads. */ + HKL layout = NtUserGetKeyboardLayout( 0 ); + if (flags & SEND_HWMSG_INJECTED) + { + scan = scan & 0xff; + if (input->ki.dwFlags & KEYEVENTF_EXTENDEDKEY) scan |= 0xe000; + } + req->input.kbd.vkey = NtUserMapVirtualKeyEx( scan, MAPVK_VSC_TO_VK_EX, layout ); + req->input.kbd.scan = input->ki.wScan & 0xff; + } + else + { + req->input.kbd.vkey = input->ki.wVk; + req->input.kbd.scan = input->ki.wScan; + } + req->input.kbd.flags = input->ki.dwFlags & ~KEYEVENTF_SCANCODE; req->input.kbd.time = input->ki.time; req->input.kbd.info = input->ki.dwExtraInfo; affects_key_state = TRUE; diff --git a/dlls/winewayland.drv/wayland_keyboard.c b/dlls/winewayland.drv/wayland_keyboard.c index 04a1c0a9fc3..55b14b1dfd5 100644 --- a/dlls/winewayland.drv/wayland_keyboard.c +++ b/dlls/winewayland.drv/wayland_keyboard.c @@ -801,9 +801,8 @@ static void send_right_control(HWND hwnd, uint32_t state) { INPUT input = {0}; input.type = INPUT_KEYBOARD; - input.ki.wScan = key2scan(KEY_RIGHTCTRL); - input.ki.wVk = VK_RCONTROL; - input.ki.dwFlags |= KEYEVENTF_EXTENDEDKEY; + input.ki.wScan = 0xe000 | (key2scan(KEY_RIGHTCTRL) & 0xff); + input.ki.dwFlags = KEYEVENTF_SCANCODE | KEYEVENTF_EXTENDEDKEY; if (state == WL_KEYBOARD_KEY_STATE_RELEASED) input.ki.dwFlags |= KEYEVENTF_KEYUP; NtUserSendHardwareInput(hwnd, 0, &input, 0); } @@ -824,8 +823,8 @@ static void keyboard_handle_key(void *data, struct wl_keyboard *wl_keyboard, if (key == KEY_RIGHTALT) send_right_control(hwnd, state);
input.type = INPUT_KEYBOARD; - input.ki.wScan = scan & 0xff; - input.ki.wVk = NtUserMapVirtualKeyEx(scan, MAPVK_VSC_TO_VK_EX, keyboard_hkl); + input.ki.wScan = (scan & 0x300) ? scan + 0xdf00 : scan; + input.ki.dwFlags = KEYEVENTF_SCANCODE; if (scan & ~0xff) input.ki.dwFlags |= KEYEVENTF_EXTENDEDKEY;
if (state == WL_KEYBOARD_KEY_STATE_RELEASED) input.ki.dwFlags |= KEYEVENTF_KEYUP;
From: Alexandros Frantzis alexandros.frantzis@collabora.com
--- dlls/win32u/input.c | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-)
diff --git a/dlls/win32u/input.c b/dlls/win32u/input.c index 38350861c6b..333c0c1e4f7 100644 --- a/dlls/win32u/input.c +++ b/dlls/win32u/input.c @@ -410,27 +410,27 @@ LONG global_key_state_counter = 0; BOOL grab_pointer = TRUE; BOOL grab_fullscreen = FALSE;
-static void kbd_tables_init_vsc2vk( const KBDTABLES *tables, BYTE vsc2vk[0x300] ) +static void kbd_tables_init_vsc2vk( const KBDTABLES *tables, USHORT vsc2vk[0x300] ) { const VSC_VK *entry; WORD vsc;
- memset( vsc2vk, 0, 0x300 ); + memset( vsc2vk, 0, 0x300 * sizeof(USHORT) );
for (vsc = 0; tables->pusVSCtoVK && vsc <= tables->bMaxVSCtoVK; ++vsc) { if (tables->pusVSCtoVK[vsc] == VK__none_) continue; - vsc2vk[vsc] = (BYTE)tables->pusVSCtoVK[vsc]; + vsc2vk[vsc] = tables->pusVSCtoVK[vsc]; } for (entry = tables->pVSCtoVK_E0; entry && entry->Vsc; entry++) { if (entry->Vk == VK__none_) continue; - vsc2vk[entry->Vsc + 0x100] = (BYTE)entry->Vk; + vsc2vk[entry->Vsc + 0x100] = entry->Vk; } for (entry = tables->pVSCtoVK_E1; entry && entry->Vsc; entry++) { if (entry->Vk == VK__none_) continue; - vsc2vk[entry->Vsc + 0x200] = (BYTE)entry->Vk; + vsc2vk[entry->Vsc + 0x200] = entry->Vk; } }
@@ -1033,7 +1033,8 @@ WORD WINAPI NtUserVkKeyScanEx( WCHAR chr, HKL layout ) */ UINT WINAPI NtUserMapVirtualKeyEx( UINT code, UINT type, HKL layout ) { - BYTE vsc2vk[0x300], vk2char[0x100]; + USHORT vsc2vk[0x300]; + BYTE vk2char[0x100]; const KBDTABLES *kbd_tables; UINT ret = 0;
@@ -1066,7 +1067,7 @@ UINT WINAPI NtUserMapVirtualKeyEx( UINT code, UINT type, HKL layout ) }
kbd_tables_init_vsc2vk( kbd_tables, vsc2vk ); - for (ret = 0; ret < ARRAY_SIZE(vsc2vk); ++ret) if (vsc2vk[ret] == code) break; + for (ret = 0; ret < ARRAY_SIZE(vsc2vk); ++ret) if ((vsc2vk[ret] & 0xff) == code) break; if (ret >= ARRAY_SIZE(vsc2vk)) ret = 0;
if (type == MAPVK_VK_TO_VSC) @@ -1082,7 +1083,7 @@ UINT WINAPI NtUserMapVirtualKeyEx( UINT code, UINT type, HKL layout )
if (code & 0xe000) code -= 0xdf00; if (code >= ARRAY_SIZE(vsc2vk)) ret = 0; - else ret = vsc2vk[code]; + else ret = vsc2vk[code] & 0xff;
if (type == MAPVK_VSC_TO_VK) { @@ -1130,15 +1131,15 @@ INT WINAPI NtUserGetKeyNameText( LONG lparam, WCHAR *buffer, INT size )
if (lparam & 0x2000000) { - BYTE vsc2vk[0x300]; + USHORT vsc2vk[0x300]; kbd_tables_init_vsc2vk( kbd_tables, vsc2vk ); - switch ((vkey = vsc2vk[code])) + switch ((vkey = vsc2vk[code] & 0xff)) { case VK_RSHIFT: case VK_RCONTROL: case VK_RMENU: for (code = 0; code < ARRAY_SIZE(vsc2vk); ++code) - if (vsc2vk[code] == (vkey - 1)) break; + if ((vsc2vk[code] & 0xff) == (vkey - 1)) break; break; } }
From: Alexandros Frantzis alexandros.frantzis@collabora.com
If the hardware keyboard event has KBD virtual key information, use it to detect numpad key events and translate them to the appropriate virtual keys depending on NumLock (and Shift) state. --- dlls/win32u/input.c | 25 +++++++++++++++++++++++++ dlls/win32u/message.c | 2 +- dlls/win32u/win32u_private.h | 1 + server/queue.c | 27 +++++++++++++++++++++++++-- 4 files changed, 52 insertions(+), 3 deletions(-)
diff --git a/dlls/win32u/input.c b/dlls/win32u/input.c index 333c0c1e4f7..84bd26379f5 100644 --- a/dlls/win32u/input.c +++ b/dlls/win32u/input.c @@ -1112,6 +1112,31 @@ UINT WINAPI NtUserMapVirtualKeyEx( UINT code, UINT type, HKL layout ) return ret; }
+/*********************************************************************** + * map_scan_to_kbd_vkey + * + * Map a scancode to a virtual key with KBD information. + */ +USHORT map_scan_to_kbd_vkey( USHORT scan, HKL layout ) +{ + const KBDTABLES *kbd_tables; + USHORT vsc2vk[0x300]; + UINT vkey; + + if ((vkey = user_driver->pMapVirtualKeyEx( scan, MAPVK_VSC_TO_VK_EX, layout )) != -1) return vkey; + + if (!(kbd_tables = user_driver->pKbdLayerDescriptor( layout ))) kbd_tables = &kbdus_tables; + + kbd_tables_init_vsc2vk( kbd_tables, vsc2vk ); + if (scan & 0xe000) scan -= 0xdf00; + if (scan >= ARRAY_SIZE(vsc2vk)) vkey = 0; + else vkey = vsc2vk[scan]; + + if (kbd_tables != &kbdus_tables) user_driver->pReleaseKbdTables( kbd_tables ); + + return vkey; +} + /**************************************************************************** * NtUserGetKeyNameText (win32u.@) */ diff --git a/dlls/win32u/message.c b/dlls/win32u/message.c index ddc9658f087..531cb8d0649 100644 --- a/dlls/win32u/message.c +++ b/dlls/win32u/message.c @@ -3569,7 +3569,7 @@ NTSTATUS send_hardware_message( HWND hwnd, UINT flags, const INPUT *input, LPARA scan = scan & 0xff; if (input->ki.dwFlags & KEYEVENTF_EXTENDEDKEY) scan |= 0xe000; } - req->input.kbd.vkey = NtUserMapVirtualKeyEx( scan, MAPVK_VSC_TO_VK_EX, layout ); + req->input.kbd.vkey = map_scan_to_kbd_vkey( scan, layout ); req->input.kbd.scan = input->ki.wScan & 0xff; } else diff --git a/dlls/win32u/win32u_private.h b/dlls/win32u/win32u_private.h index 24e8b1b498d..94b9d00b4fb 100644 --- a/dlls/win32u/win32u_private.h +++ b/dlls/win32u/win32u_private.h @@ -108,6 +108,7 @@ extern void update_mouse_tracking_info( HWND hwnd ); extern BOOL get_clip_cursor( RECT *rect ); extern BOOL process_wine_clipcursor( HWND hwnd, UINT flags, BOOL reset ); extern BOOL clip_fullscreen_window( HWND hwnd, BOOL reset ); +extern USHORT map_scan_to_kbd_vkey( USHORT scan, HKL layout );
/* menu.c */ extern HMENU create_menu( BOOL is_popup ); diff --git a/server/queue.c b/server/queue.c index cc10d0254eb..c4b9389695f 100644 --- a/server/queue.c +++ b/server/queue.c @@ -36,6 +36,7 @@ #include "winternl.h" #include "ntuser.h" #include "hidusage.h" +#include "kbd.h"
#include "handle.h" #include "file.h" @@ -1763,7 +1764,7 @@ static int send_hook_ll_message( struct desktop *desktop, struct message *hardwa
if (input->type == INPUT_KEYBOARD) { - unsigned short vkey = input->kbd.vkey; + unsigned short vkey = input->kbd.vkey & 0xff; if (input->kbd.flags & KEYEVENTF_UNICODE) vkey = VK_PACKET; msg->lparam = (input->kbd.scan << 16) | vkey; } @@ -2102,6 +2103,7 @@ static int queue_keyboard_message( struct desktop *desktop, user_handle_t win, c struct message *msg; struct thread *foreground; unsigned char vkey = input->kbd.vkey; + hw_input_t hook_input = *input; unsigned int message_code, time; int wait;
@@ -2171,6 +2173,27 @@ static int queue_keyboard_message( struct desktop *desktop, user_handle_t win, c break; }
+ /* send numpad vkeys if NumLock is active */ + if ((input->kbd.vkey & KBDNUMPAD) && (desktop->keystate[VK_NUMLOCK] & 0x01) && + !(desktop->keystate[VK_SHIFT] & 0x80)) + { + switch (vkey) + { + case VK_INSERT: hook_input.kbd.vkey = vkey = VK_NUMPAD0; break; + case VK_END: hook_input.kbd.vkey = vkey = VK_NUMPAD1; break; + case VK_DOWN: hook_input.kbd.vkey = vkey = VK_NUMPAD2; break; + case VK_NEXT: hook_input.kbd.vkey = vkey = VK_NUMPAD3; break; + case VK_LEFT: hook_input.kbd.vkey = vkey = VK_NUMPAD4; break; + case VK_CLEAR: hook_input.kbd.vkey = vkey = VK_NUMPAD5; break; + case VK_RIGHT: hook_input.kbd.vkey = vkey = VK_NUMPAD6; break; + case VK_HOME: hook_input.kbd.vkey = vkey = VK_NUMPAD7; break; + case VK_UP: hook_input.kbd.vkey = vkey = VK_NUMPAD8; break; + case VK_PRIOR: hook_input.kbd.vkey = vkey = VK_NUMPAD9; break; + case VK_DELETE: hook_input.kbd.vkey = vkey = VK_DECIMAL; break; + default: break; + } + } + if ((foreground = get_foreground_thread( desktop, win ))) { struct rawinput_message raw_msg = {0}; @@ -2217,7 +2240,7 @@ static int queue_keyboard_message( struct desktop *desktop, user_handle_t win, c msg_data->flags |= (flags & (KF_EXTENDED | KF_ALTDOWN | KF_UP)) >> 8; }
- if (!(wait = send_hook_ll_message( desktop, msg, input, sender ))) + if (!(wait = send_hook_ll_message( desktop, msg, &hook_input, sender ))) queue_hardware_message( desktop, msg, 1 );
return wait;
From: Alexandros Frantzis alexandros.frantzis@collabora.com
--- dlls/user32/tests/input.c | 72 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 69 insertions(+), 3 deletions(-)
diff --git a/dlls/user32/tests/input.c b/dlls/user32/tests/input.c index 889c24b1dcc..378a025abd0 100644 --- a/dlls/user32/tests/input.c +++ b/dlls/user32/tests/input.c @@ -615,15 +615,38 @@ static void check_keyboard_state_( int line, const BYTE expect_state[256], const } }
+static void clear_keyboard_state( void ) +{ + static BYTE empty_state[256] = {0}; + INPUT input = {.type = INPUT_KEYBOARD}; + BYTE lock_keys[] = {VK_NUMLOCK, VK_CAPITAL, VK_SCROLL}; + UINT i; + + for (i = 0; i < ARRAY_SIZE(lock_keys); ++i) + { + if (GetKeyState( lock_keys[i] ) & 0x0001) + { + input.ki.wVk = lock_keys[i]; + SendInput( 1, &input, sizeof(input) ); + input.ki.dwFlags = KEYEVENTF_KEYUP; + SendInput( 1, &input, sizeof(input) ); + wait_messages( 5, FALSE ); + memset( current_sequence, 0, sizeof(current_sequence) ); + current_sequence_len = 0; + } + } + + SetKeyboardState( empty_state ); +} + #define check_send_input_keyboard_test( a, b ) check_send_input_keyboard_test_( a, #a, b ) static void check_send_input_keyboard_test_( const struct send_input_keyboard_test *test, const char *context, BOOL peeked ) { - static BYTE empty_state[256] = {0}; INPUT input = {.type = INPUT_KEYBOARD}; UINT i;
winetest_push_context( "%s", context ); - SetKeyboardState( empty_state ); + clear_keyboard_state();
for (i = 0; test->vkey || test->scan; i++, test++) { @@ -641,7 +664,7 @@ static void check_send_input_keyboard_test_( const struct send_input_keyboard_te winetest_pop_context(); }
- SetKeyboardState( empty_state ); + clear_keyboard_state(); winetest_pop_context(); }
@@ -1146,6 +1169,45 @@ static void test_SendInput_keyboard_messages( WORD vkey, WORD scan, WCHAR wch, W {0}, };
+ struct send_input_keyboard_test numpad_scan[] = + { + {.scan = 0x4b, .flags = KEYEVENTF_SCANCODE, .expect_state = {[VK_LEFT] = 0x80}, + .expect = {KEY_HOOK(WM_KEYDOWN, 0x4b, VK_LEFT), KEY_MSG(WM_KEYDOWN, 0x4b, VK_LEFT), {0}}}, + {.scan = 0x4b, .flags = KEYEVENTF_SCANCODE | KEYEVENTF_KEYUP, + .expect = {KEY_HOOK(WM_KEYUP, 0x4b, VK_LEFT), KEY_MSG(WM_KEYUP, 0x4b, VK_LEFT), {0}}}, + {0}, + }; + + struct send_input_keyboard_test numpad_scan_numlock[] = + { + {.scan = 0x45, .flags = KEYEVENTF_SCANCODE, .expect_state = {[VK_NUMLOCK] = 0x80}, + .expect = {KEY_HOOK(WM_KEYDOWN, 0x45, VK_NUMLOCK), KEY_MSG(WM_KEYDOWN, 0x45, VK_NUMLOCK), {0}}}, + {.scan = 0x45, .flags = KEYEVENTF_SCANCODE | KEYEVENTF_KEYUP, .expect_state = {[VK_NUMLOCK] = 0x01}, + .expect = {KEY_HOOK(WM_KEYUP, 0x45, VK_NUMLOCK), KEY_MSG(WM_KEYUP, 0x45, VK_NUMLOCK), {0}}}, + { + .scan = 0x4b, .flags = KEYEVENTF_SCANCODE, + .expect_state = {[VK_NUMPAD4] = 0x80, [VK_NUMLOCK] = 0x01}, + .todo_state = {[VK_NUMPAD4] = TRUE, [VK_LEFT] = TRUE}, + .expect = + { + KEY_HOOK(WM_KEYDOWN, 0x4b, VK_NUMPAD4, .todo_value = TRUE), + KEY_MSG(WM_KEYDOWN, 0x4b, VK_NUMPAD4, .todo_value = TRUE), + WIN_MSG(WM_CHAR, '4', MAKELONG(1, 0x4b), .todo_value = TRUE), + {0} + } + }, + { + .scan = 0x4b, .flags = KEYEVENTF_SCANCODE | KEYEVENTF_KEYUP, .expect_state = {[VK_NUMLOCK] = 0x01}, + .expect = + { + KEY_HOOK(WM_KEYUP, 0x4b, VK_NUMPAD4, .todo_value = TRUE), + KEY_MSG(WM_KEYUP, 0x4b, VK_NUMPAD4, .todo_value = TRUE), + {0} + } + }, + {0}, + }; + #undef WIN_MSG #undef KBD_HOOK #undef KEY_HOOK_ @@ -1226,6 +1288,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( numpad_scan, TRUE ); + check_send_input_keyboard_test( numpad_scan_numlock, TRUE ); winetest_pop_context();
wait_messages( 100, FALSE ); @@ -1269,6 +1333,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( numpad_scan, FALSE ); + check_send_input_keyboard_test( numpad_scan_numlock, FALSE ); winetest_pop_context();
ok_ret( 1, DestroyWindow( hwnd ) );
From: Alexandros Frantzis alexandros.frantzis@collabora.com
--- dlls/winewayland.drv/wayland_keyboard.c | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-)
diff --git a/dlls/winewayland.drv/wayland_keyboard.c b/dlls/winewayland.drv/wayland_keyboard.c index 55b14b1dfd5..bdef56e8f0c 100644 --- a/dlls/winewayland.drv/wayland_keyboard.c +++ b/dlls/winewayland.drv/wayland_keyboard.c @@ -358,7 +358,7 @@ static void add_xkb_layout(const char *xkb_layout, struct xkb_keymap *xkb_keymap
unsigned int mod, keyc, len, names_len, min_keycode, max_keycode; struct xkb_state *xkb_state = xkb_state_new(xkb_keymap); - xkb_mod_mask_t shift_mask, control_mask, altgr_mask, capslock_mask; + xkb_mod_mask_t shift_mask, control_mask, altgr_mask, capslock_mask, numlock_mask; VSC_LPWSTR *names_entry, *names_ext_entry; VSC_VK *vsc2vk_e0_entry, *vsc2vk_e1_entry; VK_TO_WCHARS8 *vk2wchars_entry; @@ -493,6 +493,7 @@ static void add_xkb_layout(const char *xkb_layout, struct xkb_keymap *xkb_keymap control_mask = 1 << xkb_keymap_mod_get_index(xkb_keymap, XKB_MOD_NAME_CTRL); capslock_mask = 1 << xkb_keymap_mod_get_index(xkb_keymap, XKB_MOD_NAME_CAPS); altgr_mask = 1 << xkb_keymap_mod_get_index(xkb_keymap, "Mod5"); + numlock_mask = 1 << xkb_keymap_mod_get_index(xkb_keymap, XKB_MOD_NAME_NUM);
for (keyc = min_keycode; keyc <= max_keycode; keyc++) { @@ -502,6 +503,19 @@ static void add_xkb_layout(const char *xkb_layout, struct xkb_keymap *xkb_keymap uint32_t caps_ret, shift_ret; unsigned int mod;
+ if ((vkey & KBDNUMPAD) && (vkey & 0xff) == VK_DELETE) + { + VK_TO_WCHARS8 num_vkey2wch = {.VirtualKey = VK_DECIMAL}; + + xkb_state_update_mask(xkb_state, 0, 0, numlock_mask, 0, 0, xkb_group); + if (!(num_vkey2wch.wch[0] = xkb_state_key_get_utf32(xkb_state, keyc))) + num_vkey2wch.wch[0] = WCH_NONE; + for (mod = 1; mod < 8; ++mod) num_vkey2wch.wch[mod] = WCH_NONE; + num_vkey2wch.Attributes = 0; + TRACE("vkey %#06x -> %s\n", num_vkey2wch.VirtualKey, debugstr_wn(num_vkey2wch.wch, 8)); + *vk2wchars_entry++ = num_vkey2wch; + } + for (mod = 0; mod < 8; ++mod) { xkb_mod_mask_t mod_mask = 0;
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=145680
Your paranoid android.
=== w11pro64 (32 bit report) ===
user32: input.c:4173: Test failed: layered 0: button_down_hwnd_todo 0: got MSG_TEST_WIN hwnd 000401FC, msg WM_LBUTTONDOWN, wparam 0x1, lparam 0x320032 input.c:4173: Test failed: layered 0: button_down_hwnd_todo 1 (missing): MSG_TEST_WIN hwnd 000401FC, WM_LBUTTONDOWN, wparam 0x1, lparam 0x320032 input.c:4193: Test failed: layered 1: button_down_hwnd_todo 0: got MSG_TEST_WIN hwnd 000C00F2, msg WM_LBUTTONDOWN, wparam 0x1, lparam 0x320032 input.c:4193: Test failed: layered 1: button_down_hwnd_todo 1 (missing): MSG_TEST_WIN hwnd 000C00F2, WM_LBUTTONDOWN, wparam 0x1, lparam 0x320032
=== debian11 (32 bit report) ===
user32: input.c:4067: Test succeeded inside todo block: button_down_hwnd_todo 1: got MSG_TEST_WIN hwnd 0004009A, msg WM_LBUTTONDOWN, wparam 0x1, lparam 0x320032
=== debian11 (32 bit ja:JP report) ===
user32: input.c:4067: Test succeeded inside todo block: button_down_hwnd_todo 1: got MSG_TEST_WIN hwnd 0004009A, msg WM_LBUTTONDOWN, wparam 0x1, lparam 0x320032
=== debian11b (32 bit WoW report) ===
user32: input.c:4067: Test succeeded inside todo block: button_down_hwnd_todo 1: got MSG_TEST_WIN hwnd 0004009A, msg WM_LBUTTONDOWN, wparam 0x1, lparam 0x320032
@rbernon
I have pushed multiple versions together. The purpose of v4 is only to provide a clean diff from v3 for easier review, before the rebase in v5 and then an additional change in v6.
v4: * Skip rctrl_scan test on Korean layouts * Added numpad scan tests along with required improvements to the way we clear keyboard state for each test. Note that some expectations in these tests are TODO because winex11 doesn't handle these correctly at the moment (whereas KBDTABLES/winewayland pass cleanly).
v5: Rebase on latest master (no functionality diff from v4), so the tests can actually pass (due to the changes in !5652).
v6: Only wait for and clean events if we actually emit events during the clearing of keyboard state.
Testbot run for v6: https://testbot.winehq.org/JobDetails.pl?Key=145680
Rémi Bernon (@rbernon) commented about dlls/user32/tests/input.c:
else check_send_input_keyboard_test( menu_ext_peeked, TRUE ); check_send_input_keyboard_test( lrshift_ext, TRUE ); check_send_input_keyboard_test( rshift_scan, TRUE );
- /* Skip on Korean layouts since they map the right control key to VK_HANJA */
- if (LOWORD(hkl) == 0x0412) skip( "skipping rctrl_scan test on Korean layout" );
`LOWORD(hkl)` is the user locale, and the keyboard layout could very well be anything. I think you either want `HIWORD(hkl)`, or maybe just `hkl == (HKL)0x04120412` like the imm32 tests are doing.
Could we pass id and lparam to send_hook_ll_message instead? Keeping the vkey adjustment out of it, something like https://gitlab.winehq.org/rbernon/wine/-/commit/8af64a92bc6c286e3072f4e5a906...
On Mon May 20 11:46:26 2024 +0000, Rémi Bernon wrote:
Could we pass id and lparam to send_hook_ll_message instead? Keeping the vkey adjustment out of it, something like https://gitlab.winehq.org/rbernon/wine/-/commit/8af64a92bc6c286e3072f4e5a906...
Yes, this is better. One aspect of the change I am unsure about, is that it moves the modifier vkey remapping later in the function, so now rawinput and KF_REPEAT handling (and also the hook) will use the non-remapped modifier vkey. Perhaps the changed behavior is in fact more correct or inconsequential (but I don't know, although modifier KF_REPEAT seems broken both ways), but given that this is a refactoring commit perhaps we should try to retain the old behavior in this commit/MR?
we should try to retain the old behavior in this commit/MR?
Yeah, sounds good, lets keep the code like you wrote it.