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.
-- v2: winewayland.drv: Populate vkey to wchar entry for VK_DECIMAL. server: Send numpad virtual keys if NumLock is active. win32u: Store the full KBD vkey information in kbd_tables_init_vsc2vk. win32u: Allow drivers to send only the scan code for keyboard events.
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 | 27 +++++++++++++++++-------- dlls/win32u/message.c | 23 ++++++++++++++++++--- dlls/winewayland.drv/wayland_keyboard.c | 9 ++++----- 3 files changed, 43 insertions(+), 16 deletions(-)
diff --git a/dlls/user32/tests/input.c b/dlls/user32/tests/input.c index fcaa4229b02..08a1d69b02b 100644 --- a/dlls/user32/tests/input.c +++ b/dlls/user32/tests/input.c @@ -1070,16 +1070,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}, };
@@ -1175,7 +1185,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 */ @@ -1207,6 +1216,7 @@ 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 ); + 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 ); @@ -1247,6 +1257,7 @@ 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 ); + 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 ed099b3b989..57410f8de10 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/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=145490
Your paranoid android.
=== w10pro64_ja (64 bit report) ===
user32: input.c:638: Test failed: peek: rctrl_scan: 2: test->expect 0: got LL_HOOK_KEYBD msg WM_KEYDOWN scan 0x1d, vkey 0x19, flags 0x11, extra 0 input.c:638: Test failed: peek: rctrl_scan: 2: test->expect 1: got MSG_TEST_WIN hwnd 0000000000000000, msg WM_KEYDOWN, wparam 0xe5, lparam 0x11d0001 input.c:638: Test failed: peek: rctrl_scan: 2: test->expect 2 (spurious): got MSG_TEST_WIN hwnd 0000000000000000, msg WM_KEYDOWN, wparam 0x19, lparam 0x11d0001 input.c:639: Test failed: peek: rctrl_scan: 2: got VK_CONTROL: 0x1 input.c:639: Test failed: peek: rctrl_scan: 2: got 0x19: 0x81 input.c:639: Test failed: peek: rctrl_scan: 2: got VK_RCONTROL: 0 input.c:638: Test failed: peek: rctrl_scan: 3: test->expect 0: got LL_HOOK_KEYBD msg WM_KEYUP scan 0x1d, vkey 0x19, flags 0x91, extra 0 input.c:638: Test failed: peek: rctrl_scan: 3: test->expect 1: got MSG_TEST_WIN hwnd 0000000000000000, msg WM_KEYUP, wparam 0x19, lparam 0xc11d0001 input.c:638: Test failed: receive: rctrl_scan: 2: test->expect 0: got LL_HOOK_KEYBD msg WM_KEYDOWN scan 0x1d, vkey 0x19, flags 0x11, extra 0 input.c:638: Test failed: receive: rctrl_scan: 2: test->expect 1: got MSG_TEST_WIN hwnd 0000000000000000, msg WM_KEYDOWN, wparam 0xe5, lparam 0x11d0001 input.c:638: Test failed: receive: rctrl_scan: 2: test->expect 2 (spurious): got MSG_TEST_WIN hwnd 0000000000000000, msg WM_KEYDOWN, wparam 0x19, lparam 0x11d0001 input.c:639: Test failed: receive: rctrl_scan: 2: got VK_CONTROL: 0x1 input.c:639: Test failed: receive: rctrl_scan: 2: got 0x19: 0x81 input.c:639: Test failed: receive: rctrl_scan: 2: got VK_RCONTROL: 0 input.c:638: Test failed: receive: rctrl_scan: 3: test->expect 0: got LL_HOOK_KEYBD msg WM_KEYUP scan 0x1d, vkey 0x19, flags 0x91, extra 0 input.c:638: Test failed: receive: rctrl_scan: 3: test->expect 1: got MSG_TEST_WIN hwnd 0000000000000000, msg WM_KEYUP, wparam 0x19, lparam 0xc11d0001
=== debian11 (32 bit zh:CN report) ===
user32: input.c:638: Test failed: peek: rshift_scan: 1: test->expect 2: got MSG_TEST_WIN hwnd 00000000, msg WM_CHAR, wparam 0x46, lparam 0x210001 input.c:638: Test failed: receive: rshift_scan: 1: test->expect 2: got MSG_TEST_WIN hwnd 00000000, msg WM_CHAR, wparam 0x46, lparam 0x210001
=== debian11b (64 bit WoW report) ===
dinput: joystick8.c:5759: Test failed: input 1: WaitForSingleObject returned 0x102 joystick8.c:5760: Test failed: input 1: got 0 WM_INPUT messages joystick8.c:5763: Test failed: input 1: got dwType 0 joystick8.c:5764: Test failed: input 1: got header.dwSize 0 joystick8.c:5766: Test failed: input 1: got hDevice 0000000000000000 joystick8.c:5768: Test failed: input 1: got dwSizeHid 0 joystick8.c:5769: Test failed: input 1: got dwCount 0
On Fri May 17 16:01:15 2024 +0000, Rémi Bernon wrote:
What about doing that conditionally, as the driver can provide the full scan code and wouldn't need the KEYEVENTF_EXTENDEDKEY flag. Something like:
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;
And then add a test to the SendInput tests above showing that this flag what is used for injected input? Something like:
struct send_input_keyboard_test rctrl_scan[] = { {.scan = 0xe01d, .flags = KEYEVENTF_SCANCODE, .expect_state = {[VK_CONTROL] = 0x80, [VK_LCONTROL] = 0x80}, .todo_state = {[0] = TRUE, [VK_CONTROL] = TRUE, [VK_LCONTROL] = TRUE}, .expect = {KEY_HOOK(WM_KEYDOWN, 0x1d, VK_LCONTROL, .todo_value = TRUE), KEY_MSG(WM_KEYDOWN, 0x1d, VK_CONTROL, .todo_value = TRUE), {0}}}, {.scan = 0xe01d, .flags = KEYEVENTF_SCANCODE | KEYEVENTF_KEYUP, .expect = {KEY_HOOK(WM_KEYUP, 0x1d, VK_LCONTROL, .todo_value = TRUE), KEY_MSG(WM_KEYUP, 0x1d, VK_CONTROL, .todo_value = TRUE), {0}}}, {.scan = 0x1d, .flags = KEYEVENTF_SCANCODE | KEYEVENTF_EXTENDEDKEY, .expect_state = {[VK_CONTROL] = 0x80, [VK_RCONTROL] = 0x80}, .todo_state = {[0] = TRUE, [VK_CONTROL] = TRUE, [VK_RCONTROL] = TRUE}, .expect = {KEY_HOOK_(WM_KEYDOWN, 0x1d, VK_RCONTROL, LLKHF_EXTENDED, .todo_value = TRUE), KEY_MSG(WM_KEYDOWN, 0x11d, VK_CONTROL, .todo_value = TRUE), {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, .todo_value = TRUE), {0}}}, {0}, };
Done in v2.
On Fri May 10 16:41:17 2024 +0000, Alexandros Frantzis wrote:
changed this line in [version 2 of the diff](/wine/wine/-/merge_requests/5601/diffs?diff_id=113190&start_sha=728dfbe2dc01222da341eb9d72d68b4f57153eb4#c0e9abea64045e9b4299dedef42bd12721df8706_1070_1070)
Done in v2.
@rbernon
v2: * Apply 0xe000 to scancode on KEYEVENTF_EXTENDEDKEY only for injected input (+tests). * Use 0xff mask instead of BYTE casts when using vsc2vk entries. * Use only lower byte of vkey in the WH_KEYBOARD_LL hook. * Use the translated numpad vkeys in the WH_KEYBOARD_LL hook.
This merge request was approved by Rémi Bernon.
@rbernon I saw that testbot is complaining about new failures with this MR and started investigating: https://testbot.winehq.org/JobDetails.pl?Key=145490. The TL;DR is that I suspect there is something wrong with the keyboard layout selection in the Windows testbot setups (see below).
I assume we need to delay this MR until the testbot failures are resolved (not sure what the policy is)?
Here are my current findings for the test failures:
w10pro64_ja (64 bit input) --------------------------
https://testbot.winehq.org/JobDetails.pl?Key=145490&f310=exe64.report#k3...
This setup maps the RightControl scancode (0xe01d) to vkey 0x19 VK_HANJA/VK_KANJI instead of the expect VK_RCONTROL. From online investigations I couldn't find a Japanese keyboard layout that does this. This is behavior is only documented for Korean layouts, and looking in the testbot report I see that although the language id is 0411 (ja-JP) the reported keyboard driver is in fact the Korean "KBDKOR.dll":
input.c:5703: hkl 0000000004110411 input.c:694: L"KBDKOR.DLL" flags 0
Debian 11 32-bit zh_CN ----------------------
https://testbot.winehq.org/JobDetails.pl?Key=145490&f401=win32_zh_CN.rep...
The new test failure here complains that RightShift (scan) + vkey F maps to char capital 'F'. The actual wch/wch_shift values are not reported, but since language id is 0x0804 I assume the following snippet applies
``` static void get_test_scan( WORD vkey, WORD *scan, WCHAR *wch, WCHAR *wch_shift ) { ... /* zh_CN returns a different WM_(SYS)CHAR, possibly coming from IME */ if (HIWORD(hkl) == 0x0804) { *wch = 0x430; *wch_shift = 0x410; } } ```
This workaround and the justification seems suspicious since U+0410/U+0430 are Cyrillic capital/small 'a'. The w10pro64_zh_CN log https://testbot.winehq.org/JobDetails.pl?Key=145490&f311=exe64.report#k3... provides another explanation:
input.c:5703: hkl 0000000008040804 input.c:694: L"KBDRU.DLL" flags 0x10001
Although 0x0804 (zh-CN) is requested we end up using Russian "KBDRU.DLL". This layout indeed maps scancode 0x21 (key F on US layouts) to Cyrillic A. It seems that the zh_CN workaround is actually masking another problem with layout selection.
On Fri May 17 16:01:21 2024 +0000, Alexandros Frantzis wrote:
@rbernon I saw that testbot is complaining about new failures with this MR and started investigating: https://testbot.winehq.org/JobDetails.pl?Key=145490. The TL;DR is that I suspect there is something wrong with the keyboard layout selection in the Windows testbot setups (see below). I assume we need to delay this MR until the testbot failures are resolved (not sure what the policy is)? Here are my current findings for the test failures: w10pro64_ja (64 bit input)
https://testbot.winehq.org/JobDetails.pl?Key=145490&f310=exe64.report#k3... This setup maps the RightControl scancode (0xe01d) to vkey 0x19 VK_HANJA/VK_KANJI instead of the expect VK_RCONTROL. From online investigations I couldn't find a Japanese keyboard layout that does this. This is behavior is only documented for Korean layouts, and looking in the testbot report I see that although the language id is 0411 (ja-JP) the reported keyboard driver is in fact the Korean "KBDKOR.dll":
input.c:5703: hkl 0000000004110411 input.c:694: L"KBDKOR.DLL" flags 0
Debian 11 32-bit zh_CN
https://testbot.winehq.org/JobDetails.pl?Key=145490&f401=win32_zh_CN.rep... The new test failure here complains that RightShift (scan) + vkey F maps to char capital 'F'. The actual wch/wch_shift values are not reported, but since language id is 0x0804 I assume the following snippet applies
static void get_test_scan( WORD vkey, WORD *scan, WCHAR *wch, WCHAR *wch_shift ) { ... /* zh_CN returns a different WM_(SYS)CHAR, possibly coming from IME */ if (HIWORD(hkl) == 0x0804) { *wch = 0x430; *wch_shift = 0x410; } }
This workaround and the justification seems suspicious since U+0410/U+0430 are Cyrillic capital/small 'a'. The w10pro64_zh_CN log https://testbot.winehq.org/JobDetails.pl?Key=145490&f311=exe64.report#k3... provides another explanation:
input.c:5703: hkl 0000000008040804 input.c:694: L"KBDRU.DLL" flags 0x10001
Although 0x0804 (zh-CN) is requested we end up using Russian "KBDRU.DLL". This layout indeed maps scancode 0x21 (key F on US layouts) to Cyrillic A. It seems that the zh_CN workaround is actually masking another problem with layout selection.
Indeed, thanks for catching this, I trusted Gitlab and forgot once again to have a look at the testbot runs (which we used to have reported here but that stopped working for a while now).
Keyboard layout loading is quite messy, I'm not sure we can fix this easily. An option is to skip tests on these environments for now.