[PATCH v13 0/9] MR10963: winex11: Keyboard layouts rework, part 3
-- v13: user32/tests: Add more key map tests. user32/tests: Add more key names tests. user32/tests: Add a SendInput VK_PAUSE test. winex11: Support fixed X11 keycode to scancode conversion. winex11: Move the keyc2scan mapping array to the layout struct. winex11: Use scancode high bit to set KEYEVENTF_EXTENDEDKEY flag. win32u: Remap some scancodes to better match native behavior. win32u: Reorder KEYEVENTF_SCANCODE handling control flow. winex11: Fix Right Shift scancode. https://gitlab.winehq.org/wine/wine/-/merge_requests/10963
From: Matteo Bruni <mbruni@codeweavers.com> Shortly we're only going to use this table for the legacy vnc mapping. --- dlls/winex11.drv/keyboard.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dlls/winex11.drv/keyboard.c b/dlls/winex11.drv/keyboard.c index 3d5d2c81f03..27a9f601e78 100644 --- a/dlls/winex11.drv/keyboard.c +++ b/dlls/winex11.drv/keyboard.c @@ -1168,7 +1168,7 @@ static const WORD nonchar_key_scan[256] = 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* FFD0 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* FFD8 */ /* modifier keys */ - 0x00, 0x2A, 0x136, 0x1D, 0x11D, 0x3A, 0x00, 0x38, /* FFE0 */ + 0x00, 0x2A, 0x36, 0x1D, 0x11D, 0x3A, 0x00, 0x38, /* FFE0 */ 0x138, 0x38, 0x138, 0x15b, 0x15c, 0x00, 0x00, 0x00, /* FFE8 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* FFF0 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x153 /* FFF8 */ -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10963
From: Rémi Bernon <rbernon@codeweavers.com> --- dlls/win32u/message.c | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/dlls/win32u/message.c b/dlls/win32u/message.c index b6cdd3efa0e..515f9b328e2 100644 --- a/dlls/win32u/message.c +++ b/dlls/win32u/message.c @@ -3934,28 +3934,29 @@ NTSTATUS send_hardware_message( HWND hwnd, UINT flags, const INPUT *input, LPARA req->input.mouse.info = input->mi.dwExtraInfo; break; case INPUT_KEYBOARD: - if (input->ki.dwFlags & KEYEVENTF_SCANCODE) + req->input.kbd.vkey = input->ki.wVk; + req->input.kbd.scan = input->ki.wScan; + req->input.kbd.flags = input->ki.dwFlags; + req->input.kbd.time = input->ki.time; + req->input.kbd.info = input->ki.dwExtraInfo; + + /* Handle the scancode resolution before sending data to wineserver, as it doesn't + * have access to keyboard layout tables and needs the vkey to start hook chain. + */ + if (req->input.kbd.flags & 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; - } + + if (flags & SEND_HWMSG_INJECTED) scan = scan & 0xff; + if (req->input.kbd.flags & KEYEVENTF_EXTENDEDKEY) scan |= 0xe000; + req->input.kbd.vkey = map_scan_to_kbd_vkey( scan, 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.scan &= 0xff; + req->input.kbd.flags &= ~KEYEVENTF_SCANCODE; } - req->input.kbd.flags = input->ki.dwFlags & ~KEYEVENTF_SCANCODE; - req->input.kbd.time = input->ki.time; - req->input.kbd.info = input->ki.dwExtraInfo; break; case INPUT_HARDWARE: req->input.hw.msg = input->hi.uMsg; -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10963
From: Rémi Bernon <rbernon@codeweavers.com> Based on patches from Etaash Mathamsetty and Matteo Bruni. --- dlls/win32u/input.c | 13 +++++++++++-- dlls/win32u/message.c | 9 ++++++--- dlls/win32u/win32u_private.h | 2 +- 3 files changed, 18 insertions(+), 6 deletions(-) diff --git a/dlls/win32u/input.c b/dlls/win32u/input.c index 048950345b3..afad5e52622 100644 --- a/dlls/win32u/input.c +++ b/dlls/win32u/input.c @@ -1217,13 +1217,13 @@ UINT WINAPI NtUserMapVirtualKeyEx( UINT code, UINT type, HKL layout ) * * Map a scancode to a virtual key with KBD information. */ -USHORT map_scan_to_kbd_vkey( USHORT scan, HKL layout ) +USHORT map_scan_to_kbd_vkey( USHORT scan, HKL layout, UINT *mapped ) { 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 ((vkey = user_driver->pMapVirtualKeyEx( scan, MAPVK_VSC_TO_VK_EX, layout )) != -1) goto done; if (!(kbd_tables = user_driver->pKbdLayerDescriptor( layout ))) kbd_tables = &kbdus_tables; @@ -1234,6 +1234,15 @@ USHORT map_scan_to_kbd_vkey( USHORT scan, HKL layout ) if (kbd_tables != &kbdus_tables) user_driver->pReleaseKbdTables( kbd_tables ); + /* remap some scancodes as native does */ +done: + switch (vkey & 0xff) + { + case VK_PAUSE: *mapped = 0x45; break; + case VK_RSHIFT: *mapped = 0x136; break; + case VK_NUMLOCK: *mapped = 0x145; break; + default: *mapped = scan; break; + } return vkey; } diff --git a/dlls/win32u/message.c b/dlls/win32u/message.c index 515f9b328e2..78a4821f573 100644 --- a/dlls/win32u/message.c +++ b/dlls/win32u/message.c @@ -3945,7 +3945,7 @@ NTSTATUS send_hardware_message( HWND hwnd, UINT flags, const INPUT *input, LPARA */ if (req->input.kbd.flags & KEYEVENTF_SCANCODE) { - UINT scan = input->ki.wScan; + UINT scan = input->ki.wScan, dummy; /* TODO: Use the keyboard layout of the target hwnd, once * NtUserGetKeyboardLayout supports non-current threads. */ HKL layout = NtUserGetKeyboardLayout( 0 ); @@ -3953,8 +3953,11 @@ NTSTATUS send_hardware_message( HWND hwnd, UINT flags, const INPUT *input, LPARA if (flags & SEND_HWMSG_INJECTED) scan = scan & 0xff; if (req->input.kbd.flags & KEYEVENTF_EXTENDEDKEY) scan |= 0xe000; - req->input.kbd.vkey = map_scan_to_kbd_vkey( scan, layout ); - req->input.kbd.scan &= 0xff; + req->input.kbd.vkey = map_scan_to_kbd_vkey( scan, layout, (flags & SEND_HWMSG_INJECTED) ? &dummy : &scan ); + if (scan & ~0xff) req->input.kbd.flags |= KEYEVENTF_EXTENDEDKEY; + else req->input.kbd.flags &= ~KEYEVENTF_EXTENDEDKEY; + + req->input.kbd.scan = scan & 0xff; req->input.kbd.flags &= ~KEYEVENTF_SCANCODE; } break; diff --git a/dlls/win32u/win32u_private.h b/dlls/win32u/win32u_private.h index c217aa9ead6..bc70d8b4bd1 100644 --- a/dlls/win32u/win32u_private.h +++ b/dlls/win32u/win32u_private.h @@ -110,7 +110,7 @@ extern void update_mouse_tracking_info( HWND hwnd ); extern void update_current_mouse_window( HWND hwnd, INT hittest, POINT pos ); 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 ); +extern USHORT map_scan_to_kbd_vkey( USHORT scan, HKL layout, UINT *mapped ); /* menu.c */ extern UINT draw_nc_menu_bar( HDC hdc, RECT *rect, HWND hwnd ); -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10963
From: Rémi Bernon <rbernon@codeweavers.com> Apparently X11DRV_KeymapNotify() was checking the wrong bit. Fixes: 2bfe81e41f93ce75139e3a6a2d0b68eb2dcb8fa6 --- dlls/winex11.drv/keyboard.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/dlls/winex11.drv/keyboard.c b/dlls/winex11.drv/keyboard.c index 27a9f601e78..788003fde1c 100644 --- a/dlls/winex11.drv/keyboard.c +++ b/dlls/winex11.drv/keyboard.c @@ -1339,7 +1339,7 @@ BOOL X11DRV_KeymapNotify( HWND hwnd, XEvent *event ) if (!keys[vkey & 0xff].vkey) { keys[vkey & 0xff].vkey = vkey; - keys[vkey & 0xff].scan = keyc2scan[keycode] & 0xff; + keys[vkey & 0xff].scan = keyc2scan[keycode]; } if (event->xkeymap.key_vector[i] & (1<<j)) keys[vkey & 0xff].pressed = TRUE; @@ -1374,7 +1374,7 @@ BOOL X11DRV_KeymapNotify( HWND hwnd, XEvent *event ) { TRACE( "Sending KEYUP for a modifier %#.2x\n", vkey); flags = KEYEVENTF_KEYUP; - if (keys[vkey].vkey & 0x1000) flags |= KEYEVENTF_EXTENDEDKEY; + if (keys[vkey].scan & 0x100) flags |= KEYEVENTF_EXTENDEDKEY; X11DRV_send_keyboard_input( keymapnotify_hwnd, vkey, keys[vkey].scan, flags, NtGetTickCount() ); } @@ -1461,7 +1461,7 @@ BOOL X11DRV_KeyEvent( HWND hwnd, XEvent *xev ) char buf[24]; char *Str = buf; KeySym keysym = 0; - WORD vkey = 0, bScan; + WORD vkey = 0, scan; DWORD dwFlags; int ascii_chars; XIC xic = X11DRV_get_ic( hwnd ); @@ -1528,10 +1528,10 @@ BOOL X11DRV_KeyEvent( HWND hwnd, XEvent *xev ) vkey = EVENT_event_to_vkey(xic,event); /* X returns keycode 0 for composed characters */ if (!vkey && ascii_chars) vkey = VK_NONAME; - bScan = keyc2scan[event->keycode] & 0xFF; + scan = keyc2scan[event->keycode]; - TRACE_(key)("keycode %u converted to vkey 0x%X scan %02x\n", - event->keycode, vkey, bScan); + TRACE_(key)("keycode %u converted to vkey 0x%X scan %04x\n", + event->keycode, vkey, scan); pthread_mutex_unlock( &kbd_mutex ); @@ -1539,11 +1539,11 @@ BOOL X11DRV_KeyEvent( HWND hwnd, XEvent *xev ) dwFlags = 0; if ( event->type == KeyRelease ) dwFlags |= KEYEVENTF_KEYUP; - if ( vkey & 0x100 ) dwFlags |= KEYEVENTF_EXTENDEDKEY; + if ( scan & 0x100 ) dwFlags |= KEYEVENTF_EXTENDEDKEY; update_lock_state( hwnd, vkey, event->state, event_time ); - X11DRV_send_keyboard_input( hwnd, vkey & 0xff, bScan, dwFlags, event_time ); + X11DRV_send_keyboard_input( hwnd, vkey & 0xff, scan & 0xff, dwFlags, event_time ); return TRUE; } -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10963
From: Matteo Bruni <mbruni@codeweavers.com> --- dlls/winex11.drv/keyboard.c | 206 +++++++++++++++++++++++++++++---- dlls/winex11.drv/x11drv.h | 2 + dlls/winex11.drv/x11drv_main.c | 1 + 3 files changed, 184 insertions(+), 25 deletions(-) diff --git a/dlls/winex11.drv/keyboard.c b/dlls/winex11.drv/keyboard.c index 788003fde1c..c3e3d2d4659 100644 --- a/dlls/winex11.drv/keyboard.c +++ b/dlls/winex11.drv/keyboard.c @@ -76,12 +76,19 @@ struct layout LANGID lang; DWORD klid; WORD layout_id; + + WORD keyc2scan[256]; }; static const unsigned int ControlMask = 1 << 2; static int min_keycode, max_keycode, keysyms_per_keycode; -static WORD keyc2vkey[256], keyc2scan[256]; +static WORD keyc2vkey[256]; + +static WORD keyc2scan( unsigned int keycode, unsigned int state, const WORD *scancodes ) +{ + return scancodes[keycode]; +} static int NumLockMask, ScrollLockMask, AltGrMask; /* mask in the XKeyEvent state */ @@ -141,7 +148,9 @@ done: return layout_id; } -static void create_layout_from_xkb( int xkb_group, const char *xkb_layout, LANGID lang, DWORD klid ) +static void init_scancodes_table( Display *display, WORD scancodes[256], unsigned int xkb_group ); + +static void create_layout_from_xkb( Display *display, int xkb_group, const char *xkb_layout, LANGID lang, DWORD klid ) { struct layout *layout; WORD index = 0; @@ -174,6 +183,8 @@ static void create_layout_from_xkb( int xkb_group, const char *xkb_layout, LANGI layout->klid = !klid ? MAKELONG(lang, 0) : (klid == -1) ? MAKELONG(lang, index + 0x20) : klid; layout->layout_id = get_layout_id_from_klid( klid ); + init_scancodes_table( display, layout->keyc2scan, xkb_group ); + TRACE( "Created layout entry %p, lang %04x, klid %08x, layout_id %04x\n", layout, layout->lang, layout->klid, layout->layout_id ); } @@ -1314,6 +1325,8 @@ BOOL X11DRV_KeymapNotify( HWND hwnd, XEvent *event ) WORD pressed; } keys[256]; struct x11drv_thread_data *thread_data = x11drv_thread_data(); + const struct layout *layout = thread_data->layout; + const WORD *scancodes = layout->keyc2scan; keymapnotify_hwnd = thread_data->keymapnotify_hwnd; thread_data->keymapnotify_hwnd = NULL; @@ -1339,7 +1352,7 @@ BOOL X11DRV_KeymapNotify( HWND hwnd, XEvent *event ) if (!keys[vkey & 0xff].vkey) { keys[vkey & 0xff].vkey = vkey; - keys[vkey & 0xff].scan = keyc2scan[keycode]; + keys[vkey & 0xff].scan = keyc2scan( keycode, 0, scancodes ); } if (event->xkeymap.key_vector[i] & (1<<j)) keys[vkey & 0xff].pressed = TRUE; @@ -1457,6 +1470,9 @@ static void update_lock_state( HWND hwnd, WORD vkey, UINT state, UINT time ) */ BOOL X11DRV_KeyEvent( HWND hwnd, XEvent *xev ) { + struct x11drv_thread_data *thread_data = x11drv_thread_data(); + const struct layout *layout = thread_data->layout; + const WORD *scancodes = layout->keyc2scan; XKeyEvent *event = &xev->xkey; char buf[24]; char *Str = buf; @@ -1525,10 +1541,10 @@ BOOL X11DRV_KeyEvent( HWND hwnd, XEvent *xev ) if (buf != Str) free( Str ); + scan = keyc2scan( event->keycode, event->state, scancodes ); vkey = EVENT_event_to_vkey(xic,event); /* X returns keycode 0 for composed characters */ if (!vkey && ascii_chars) vkey = VK_NONAME; - scan = keyc2scan[event->keycode]; TRACE_(key)("keycode %u converted to vkey 0x%X scan %04x\n", event->keycode, vkey, scan); @@ -1669,6 +1685,17 @@ static LANGID langid_from_xkb_layout( const char *layout ) return MAKELANGID(LANG_NEUTRAL, SUBLANG_CUSTOM_UNSPECIFIED); }; +static struct layout *get_layout_from_xkb_group( int xkb_group ) +{ + struct layout *layout; + + LIST_FOR_EACH_ENTRY( layout, &xkb_layouts, struct layout, entry ) + if (layout->xkb_group == xkb_group) return layout; + + WARN( "Failed to find layout for group %d\n", xkb_group ); + return NULL; +} + static const char *xkb_layout_from_langid( LANGID langid ) { for (int i = 0; i < ARRAY_SIZE(layout_ids); i++) @@ -1837,6 +1864,130 @@ static unsigned int detect_keyboard_layout( Display *display, XModifierKeymap *m return best_layout; } +static void init_scancodes_table( Display *display, WORD scancodes[256], unsigned int xkb_group ) +{ + KeySym keysym; + XKeyEvent e2; + WORD scan; + int keyc, i, keyn, syms; + char ckey[4] = { 0 }; + const char (*lkey)[MAIN_LEN][4]; + XModifierKeymap *modmap; + unsigned int state, mod, dummy, altgr_mod = 0; + + modmap = XGetModifierMapping( display ); + for (mod = 0; mod < 8 * modmap->max_keypermod; mod++) + { + int xmod = 1 << (mod / modmap->max_keypermod); + + if (!(keyc = modmap->modifiermap[mod])) continue; + XkbLookupKeySym( display, keyc, xkb_group * 0x2000, &dummy, &keysym ); + if (keysym == XK_ISO_Level3_Shift) altgr_mod = xmod; + } + TRACE( "AltGr is mapped to mod %#x\n", altgr_mod ); + XFreeModifiermap( modmap ); + + lkey = main_key_tab[kbd_layout].key; + syms = (keysyms_per_keycode > 4) ? 4 : keysyms_per_keycode; + + e2.display = display; + e2.state = xkb_group * 0x2000; + e2.type = KeyPress; + + for (keyc = min_keycode; keyc <= max_keycode; keyc++) + { + char buf[30]; + int have_chars; + + keysym = 0; + e2.keycode = (KeyCode)keyc; + have_chars = XLookupString(&e2, buf, sizeof(buf), &keysym, NULL); + scan = 0; + if (keysym) /* otherwise, keycode not used */ + { + if ((keysym >> 8) == 0xff) /* non-character key */ + { + scan = nonchar_key_scan[keysym & 0xff]; + } + else if ((keysym >> 8) == 0x1008ff) /* XFree86 vendor keys */ + { + scan = 0x100; + } + else if (keysym == 0x20) /* Spacebar */ + { + scan = 0x39; + } + else if (have_chars) + { + /* we seem to need to search the layout-dependent scancodes */ + int maxlen = 0, maxval = -1, ok; + + for (i = 0; i < syms; i++) + { + state = xkb_group << 13 | (i & 1 ? ShiftMask : 0) | (i & 2 ? altgr_mod : 0); + XkbLookupKeySym( display, keyc, state, &dummy, &keysym ); + if ((keysym < 0x8000) && (keysym != ' ')) + { + if (!XkbTranslateKeySym(display, &keysym, 0, &ckey[i], 1, NULL)) + { + /* FIXME: query what keysym is used as Mode_switch, fill XKeyEvent + * with appropriate ShiftMask and Mode_switch, use XLookupString + * to get character in the local encoding. + */ + ckey[i] = (keysym <= 0x7f) ? keysym : 0; + } + } + else + { + ckey[i] = KEYBOARD_MapDeadKeysym(keysym); + } + } + /* find key with longest match streak */ + for (keyn = 0; keyn < MAIN_LEN; keyn++) + { + for (ok = (*lkey)[keyn][i = 0]; ok && (i < 4); i++) + if ((*lkey)[keyn][i] && (*lkey)[keyn][i] != ckey[i]) ok = 0; + if (!ok) i--; /* we overshot */ + if (ok || (i > maxlen)) + { + maxlen = i; + maxval = keyn; + } + if (ok) break; + } + if (maxval >= 0) + { + /* got it */ + const WORD (*lscan)[MAIN_LEN] = main_key_tab[kbd_layout].scan; + scan = (*lscan)[maxval]; + } + } + } + TRACE("keycode %u => scancode %04x\n", keyc, scan); + scancodes[keyc] = scan; + } /* for */ + + /* If some keys still lack scancodes, assign some arbitrary ones to them now */ + for (scan = 0x60, keyc = min_keycode; keyc <= max_keycode; keyc++) + { + const char *ksname; + + if (scancodes[keyc]) continue; + + e2.keycode = (KeyCode)keyc; + keysym = XLookupKeysym(&e2, 0); + if (!keysym) continue; + + keysym = XkbKeycodeToKeysym( display, keyc, 0, 0 ); + ksname = XKeysymToString(keysym); + if (!ksname) ksname = "NoSymbol"; + + /* should make sure the scancode is unassigned here, but >=0x60 currently always is */ + + TRACE_(key)("assigning scancode %02x to unidentified keycode %u (%s)\n", scan, keyc, ksname); + scancodes[keyc] = scan++; + } +} /* initialize keyc2scan and keyc2vkey */ static void init_keycode_mappings( Display *display ) @@ -1945,7 +2096,6 @@ static void init_keycode_mappings( Display *display ) } TRACE("keycode %u => vkey %04X\n", e2.keycode, vkey); keyc2vkey[e2.keycode] = vkey; - keyc2scan[e2.keycode] = scan; if ((vkey & 0xff) && vkey_used[(vkey & 0xff)]) WARN("vkey %04X is being used by more than one keycode\n", vkey); vkey_used[(vkey & 0xff)] = 1; @@ -2054,20 +2204,6 @@ static void init_keycode_mappings( Display *display ) vkey_used[vkey] = 1; } /* for */ #undef VKEY_IF_NOT_USED - - /* If some keys still lack scancodes, assign some arbitrary ones to them now */ - for (scan = 0x60, keyc = min_keycode; keyc <= max_keycode; keyc++) - if (keyc2vkey[keyc]&&!keyc2scan[keyc]) { - const char *ksname; - keysym = XkbKeycodeToKeysym( display, keyc, 0, 0 ); - ksname = XKeysymToString(keysym); - if (!ksname) ksname = "NoSymbol"; - - /* should make sure the scancode is unassigned here, but >=0x60 currently always is */ - - TRACE_(key)("assigning scancode %02x to unidentified keycode %u (%s)\n",scan,keyc,ksname); - keyc2scan[keyc]=scan++; - } } static void find_xkb_layout_variant( Display *display, XModifierKeymap *mmp, int group, const char *name, @@ -2180,7 +2316,7 @@ void init_keyboard_layouts( Display *display ) debugstr_a(layout), debugstr_a(variant), lang, klid ); snprintf( buffer, ARRAY_SIZE(buffer), "%s:%s", layout, variant ); - create_layout_from_xkb( i, buffer, lang, klid ); + create_layout_from_xkb( display, i, buffer, lang, klid ); if (names[i]) XFree( names[i] ); } @@ -2215,6 +2351,21 @@ BOOL X11DRV_ActivateKeyboardLayout(HKL hkl, UINT flags) return TRUE; } +void x11drv_keyboard_init_thread( struct x11drv_thread_data *data ) +{ + unsigned int xkb_group; + XkbStateRec xkb_state; + Status status; + + XkbUseExtension( data->display, NULL, NULL ); + XkbSetDetectableAutoRepeat( data->display, True, NULL ); + init_keyboard_layouts( data->display ); + status = XkbGetState( data->display, XkbUseCoreKbd, &xkb_state ); + xkb_group = status ? 0 : xkb_state.group; + TRACE( "current group %u (status %#x)\n", xkb_group, status ); + + data->layout = get_layout_from_xkb_group( xkb_group ); +} /*********************************************************************** * X11DRV_MappingNotify @@ -2364,6 +2515,9 @@ SHORT X11DRV_VkKeyScanEx( WCHAR wChar, HKL hkl ) */ UINT X11DRV_MapVirtualKeyEx( UINT wCode, UINT wMapType, HKL hkl ) { + struct x11drv_thread_data *thread_data = x11drv_thread_data(); + const struct layout *layout = thread_data->layout; + const WORD *scancodes = layout->keyc2scan; UINT ret = 0; int keyc; Display *display = thread_init_display(); @@ -2388,7 +2542,7 @@ UINT X11DRV_MapVirtualKeyEx( UINT wCode, UINT wMapType, HKL hkl ) { if ((keyc2vkey[keyc] & 0xFF) == wCode) { - ret = keyc2scan[keyc] & 0xFF; + ret = keyc2scan( keyc, 0, scancodes ) & 0xFF; break; } } @@ -2404,7 +2558,7 @@ UINT X11DRV_MapVirtualKeyEx( UINT wCode, UINT wMapType, HKL hkl ) /* let's do scan -> keycode -> vkey */ for (keyc = min_keycode; keyc <= max_keycode; keyc++) - if ((keyc2scan[keyc] & 0xFF) == (wCode & 0xFF)) + if ((keyc2scan ( keyc, 0, scancodes ) & 0xFF) == (wCode & 0xFF)) { ret = keyc2vkey[keyc] & 0xFF; /* Only stop if it's not a numpad vkey; otherwise keep @@ -2506,6 +2660,9 @@ UINT X11DRV_MapVirtualKeyEx( UINT wCode, UINT wMapType, HKL hkl ) */ INT X11DRV_GetKeyNameText( LONG lParam, LPWSTR lpBuffer, INT nSize ) { + struct x11drv_thread_data *thread_data = x11drv_thread_data(); + const struct layout *layout = thread_data->layout; + const WORD *scancodes = layout->keyc2scan; Display *display = thread_init_display(); int vkey, ansi, scanCode; KeyCode keyc; @@ -2575,9 +2732,8 @@ INT X11DRV_GetKeyNameText( LONG lParam, LPWSTR lpBuffer, INT nSize ) pthread_mutex_lock( &kbd_mutex ); - for (keyi=min_keycode; keyi<=max_keycode; keyi++) - if ((keyc2scan[keyi]) == scanCode) - break; + for (keyi = min_keycode; keyi <= max_keycode; keyi++) + if (keyc2scan( keyi, 0, scancodes ) == scanCode) break; if (keyi <= max_keycode) { INT rc; diff --git a/dlls/winex11.drv/x11drv.h b/dlls/winex11.drv/x11drv.h index 56d6c814aad..a425089ff37 100644 --- a/dlls/winex11.drv/x11drv.h +++ b/dlls/winex11.drv/x11drv.h @@ -418,6 +418,7 @@ struct x11drv_thread_data HWND grab_hwnd; /* window that currently grabs the mouse */ HWND last_focus; /* last window that had focus */ HWND keymapnotify_hwnd; /* window that should receive modifier release events */ + struct layout *layout; /* layout struct for the active keyboard layout */ XIM xim; /* input method */ HWND last_xic_hwnd; /* last xic window */ XFontSet font_set; /* international text drawing font set */ @@ -769,6 +770,7 @@ extern void reapply_cursor_clipping(void); extern void ungrab_clipping_window(void); extern void move_resize_window( HWND hwnd, int dir, POINT pos ); extern void x11drv_init_keyboard( Display *display ); +extern void x11drv_keyboard_init_thread( struct x11drv_thread_data *data ); extern BOOL X11DRV_ProcessEvents( DWORD mask ); typedef int (*x11drv_error_callback)( Display *display, XErrorEvent *event, void *arg ); diff --git a/dlls/winex11.drv/x11drv_main.c b/dlls/winex11.drv/x11drv_main.c index 39b23ffe0ec..9c526f80cf9 100644 --- a/dlls/winex11.drv/x11drv_main.c +++ b/dlls/winex11.drv/x11drv_main.c @@ -760,6 +760,7 @@ struct x11drv_thread_data *x11drv_init_thread_data(void) pthread_setspecific( x11drv_thread_data_key, data ); XSelectInput( data->display, DefaultRootWindow( data->display ), PropertyChangeMask ); + x11drv_keyboard_init_thread( data ); if (use_xim) xim_thread_attach( data ); x11drv_xinput2_init( data ); net_supported_init( data ); -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10963
From: Rémi Bernon <rbernon@codeweavers.com> X11 keycodes are just Linux keycodes + 8 nowadays according to evdev or libinput drivers, and we can avoid inaccurate reconstruction in the most common case. Add special handling for PAUSE, BREAK, NUMLOCK where necessary. Keep some support for legacy X11 servers not following the modern evdev mapping. This adds a dependency on linux/input.h or dev/evdev/input.h, on Linux and FreeBSD respectively. --- configure.ac | 3 +- dlls/dinput/tests/device8.c | 2 +- dlls/winex11.drv/keyboard.c | 323 +++++++++++++++++++++--------------- 3 files changed, 196 insertions(+), 132 deletions(-) diff --git a/configure.ac b/configure.ac index 266eae64d8a..5714421a45b 100644 --- a/configure.ac +++ b/configure.ac @@ -731,7 +731,8 @@ AC_CHECK_HEADERS(\ asm/termbits.h \ asm/types.h \ asm/user.h \ - bluetooth/bluetooth.h \ + bluetooth/bluetooth.h \ + dev/evdev/input.h \ elf.h \ gettext-po.h \ link.h \ diff --git a/dlls/dinput/tests/device8.c b/dlls/dinput/tests/device8.c index 37ed9428561..e1a96558ad5 100644 --- a/dlls/dinput/tests/device8.c +++ b/dlls/dinput/tests/device8.c @@ -2486,7 +2486,7 @@ static void test_scan_codes( IDirectInputDevice8W *device, HANDLE event, HWND hw hr = IDirectInputDevice8_GetProperty( device, DIPROP_SCANCODE, &prop_dword.diph ); if (!map[j].found) - todo_wine ok( hr == DIERR_NOTFOUND, "GetProperty DIPROP_SCANCODE returned %#lx\n", hr ); + ok( hr == DIERR_NOTFOUND, "GetProperty DIPROP_SCANCODE returned %#lx\n", hr ); else if (version < 0x0800) ok( hr == DIERR_UNSUPPORTED, "GetProperty DIPROP_SCANCODE returned %#lx\n", hr ); else diff --git a/dlls/winex11.drv/keyboard.c b/dlls/winex11.drv/keyboard.c index c3e3d2d4659..e732a2d937b 100644 --- a/dlls/winex11.drv/keyboard.c +++ b/dlls/winex11.drv/keyboard.c @@ -57,6 +57,14 @@ #include "wine/debug.h" #include "wine/list.h" +#ifdef HAVE_LINUX_INPUT_H +#include <linux/input.h> +#elif defined HAVE_DEV_EVDEV_INPUT_H +#include <dev/evdev/input.h> +#else +#define KEY_KPDOT 83 +#endif + /* log format (add 0-padding as appropriate): keycode %u as in output from xev keysym %lx as in X11/keysymdef.h @@ -77,7 +85,8 @@ struct layout DWORD klid; WORD layout_id; - WORD keyc2scan[256]; + WORD fuzzy_keyc2scan[256]; + BOOL use_fuzzy_keyc2scan; }; static const unsigned int ControlMask = 1 << 2; @@ -87,7 +96,99 @@ static WORD keyc2vkey[256]; static WORD keyc2scan( unsigned int keycode, unsigned int state, const WORD *scancodes ) { - return scancodes[keycode]; + unsigned int key = keycode - 8; + + if (scancodes) + return scancodes[keycode]; + + /* base keys can be mapped directly */ + if (key <= KEY_KPDOT) return key; + +#ifdef KEY_SYSRQ + /* map keys found in KBDTABLES definitions (Txx Xxx Yxx macros) */ + switch (key) + { + case 84 /* ISO_Level3_Shift */: return 0x005a; /* T5A / VK_OEM_WSCTRL */ + case KEY_SYSRQ: return 0x0054; /* T54 / VK_SNAPSHOT */ + case KEY_102ND: return 0x0056; /* T56 / VK_OEM_102 */ + case KEY_F11: return 0x0057; /* T57 / VK_F11 */ + case KEY_F12: return 0x0058; /* T58 / VK_F12 */ + case KEY_LINEFEED: return 0x0059; /* T59 / VK_CLEAR */ + case KEY_EXIT: return 0x005b; /* T5B / VK_OEM_FINISH */ + case KEY_OPEN: return 0x005c; /* T5C / VK_OEM_JUMP */ + /* FIXME: map a KEY to T5D / VK_EREOF */ + /* FIXME: map a KEY to T5E / VK_OEM_BACKTAB */ + case KEY_COMPOSE: return 0x005f; /* T5F / VK_OEM_AUTO */ + case KEY_SCALE: return 0x0062; /* T62 / VK_ZOOM */ + case KEY_HELP: return 0x0063; /* T63 / VK_HELP */ + case KEY_F13: return 0x0064; /* T64 / VK_F13 */ + case KEY_F14: return 0x0065; /* T65 / VK_F14 */ + case KEY_F15: return 0x0066; /* T66 / VK_F15 */ + case KEY_F16: return 0x0067; /* T67 / VK_F16 */ + case KEY_F17: return 0x0068; /* T68 / VK_F17 */ + case KEY_F18: return 0x0069; /* T69 / VK_F18 */ + case KEY_F19: return 0x006a; /* T6A / VK_F19 */ + case KEY_F20: return 0x006b; /* T6B / VK_F20 */ + case KEY_F21: return 0x006c; /* T6C / VK_F21 */ + case KEY_F22: return 0x006d; /* T6D / VK_F22 */ + case KEY_F23: return 0x006e; /* T6E / VK_F23 */ + /* FIXME: map a KEY to T6F / VK_OEM_PA3 */ + case KEY_COMPUTER: return 0x0071; /* T71 / VK_OEM_RESET */ + /* FIXME: map a KEY to T73 / VK_ABNT_C1 */ + case KEY_F24: return 0x0076; /* T76 / VK_F24 */ + case KEY_KPPLUSMINUS: return 0x007b; /* T7B / VK_OEM_PA1 */ + /* FIXME: map a KEY to T7C / VK_TAB */ + /* FIXME: map a KEY to T7E / VK_ABNT_C2 */ + /* FIXME: map a KEY to T7F / VK_OEM_PA2 */ + case KEY_PREVIOUSSONG: return 0x0110; /* X10 / VK_MEDIA_PREV_TRACK */ + case KEY_NEXTSONG: return 0x0119; /* X19 / VK_MEDIA_NEXT_TRACK */ + case KEY_KPENTER: return 0x011c; /* X1C / VK_RETURN */ + case KEY_RIGHTCTRL: return 0x011d; /* X1D / VK_RCONTROL */ + case KEY_MUTE: return 0x0120; /* X20 / VK_VOLUME_MUTE */ + case KEY_PROG2: return 0x0121; /* X21 / VK_LAUNCH_APP2 */ + case KEY_PLAYPAUSE: return 0x0122; /* X22 / VK_MEDIA_PLAY_PAUSE */ + case KEY_STOPCD: return 0x0124; /* X24 / VK_MEDIA_STOP */ + case KEY_VOLUMEDOWN: return 0x012e; /* X2E / VK_VOLUME_DOWN */ + case KEY_VOLUMEUP: return 0x0130; /* X30 / VK_VOLUME_UP */ + case KEY_HOMEPAGE: return 0x0132; /* X32 / VK_BROWSER_HOME */ + case KEY_KPSLASH: return 0x0135; /* X35 / VK_DIVIDE */ + case KEY_PRINT: return 0x0137; /* X37 / VK_SNAPSHOT */ + case KEY_RIGHTALT: return 0x0138; /* X38 / VK_RMENU */ + case KEY_CANCEL: return 0x0146; /* X46 / VK_CANCEL */ + case KEY_HOME: return 0x0147; /* X47 / VK_HOME */ + case KEY_UP: return 0x0148; /* X48 / VK_UP */ + case KEY_PAGEUP: return 0x0149; /* X49 / VK_PRIOR */ + case KEY_LEFT: return 0x014b; /* X4B / VK_LEFT */ + case KEY_RIGHT: return 0x014d; /* X4D / VK_RIGHT */ + case KEY_END: return 0x014f; /* X4F / VK_END */ + case KEY_DOWN: return 0x0150; /* X50 / VK_DOWN */ + case KEY_PAGEDOWN: return 0x0151; /* X51 / VK_NEXT */ + case KEY_INSERT: return 0x0152; /* X52 / VK_INSERT */ + case KEY_DELETE: return 0x0153; /* X53 / VK_DELETE */ + case KEY_LEFTMETA: return 0x015b; /* X5B / VK_LWIN */ + case KEY_RIGHTMETA: return 0x015c; /* X5C / VK_RWIN */ + case KEY_MENU: return 0x015d; /* X5D / VK_APPS */ + case KEY_POWER: return 0x015e; /* X5E / VK_POWER */ + case KEY_SLEEP: return 0x015f; /* X5F / VK_SLEEP */ + case KEY_FIND: return 0x0165; /* X65 / VK_BROWSER_SEARCH */ + case KEY_BOOKMARKS: return 0x0166; /* X66 / VK_BROWSER_FAVORITES */ + case KEY_REFRESH: return 0x0167; /* X67 / VK_BROWSER_REFRESH */ + case KEY_STOP: return 0x0168; /* X68 / VK_BROWSER_STOP */ + case KEY_FORWARD: return 0x0169; /* X69 / VK_BROWSER_FORWARD */ + case KEY_BACK: return 0x016a; /* X6A / VK_BROWSER_BACK */ + case KEY_PROG1: return 0x016b; /* X6B / VK_LAUNCH_APP1 */ + case KEY_MAIL: return 0x016c; /* X6C / VK_LAUNCH_MAIL */ + case KEY_MEDIA: return 0x016d; /* X6D / VK_LAUNCH_MEDIA_SELECT */ + case KEY_PAUSE: + if (state & ControlMask) return 0x0146; /* X46 / VK_CANCEL */ + return 0x021d; /* Y1D / VK_PAUSE */ + } +#else + FIXME( "evdev keycodes not available, scancode mapping is going to be broken\n" ); +#endif + + /* otherwise just make up some extended scancode */ + return 0x200 | (key & 0x7f); } static int NumLockMask, ScrollLockMask, AltGrMask; /* mask in the XKeyEvent state */ @@ -148,7 +249,12 @@ done: return layout_id; } -static void init_scancodes_table( Display *display, WORD scancodes[256], unsigned int xkb_group ); +static const WORD *get_layout_fuzzy_scancodes( const struct layout *layout ) +{ + return layout->use_fuzzy_keyc2scan ? layout->fuzzy_keyc2scan : NULL; +} + +static BOOL init_scancodes_table( Display *display, WORD scancodes[256], unsigned int xkb_group ); static void create_layout_from_xkb( Display *display, int xkb_group, const char *xkb_layout, LANGID lang, DWORD klid ) { @@ -183,53 +289,14 @@ static void create_layout_from_xkb( Display *display, int xkb_group, const char layout->klid = !klid ? MAKELONG(lang, 0) : (klid == -1) ? MAKELONG(lang, index + 0x20) : klid; layout->layout_id = get_layout_id_from_klid( klid ); - init_scancodes_table( display, layout->keyc2scan, xkb_group ); + if (strstr(xkb_layout, ":vnc")) + layout->use_fuzzy_keyc2scan = init_scancodes_table( display, layout->fuzzy_keyc2scan, xkb_group ); TRACE( "Created layout entry %p, lang %04x, klid %08x, layout_id %04x\n", layout, layout->lang, layout->klid, layout->layout_id ); } /* Keyboard translation tables */ #define MAIN_LEN 49 -static const WORD main_key_scan_qwerty[MAIN_LEN] = -{ -/* this is my (102-key) keyboard layout, sorry if it doesn't quite match yours */ - /* ` 1 2 3 4 5 6 7 8 9 0 - = */ - 0x29,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0A,0x0B,0x0C,0x0D, - /* q w e r t y u i o p [ ] */ - 0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1A,0x1B, - /* a s d f g h j k l ; ' \ */ - 0x1E,0x1F,0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28,0x2B, - /* z x c v b n m , . / */ - 0x2C,0x2D,0x2E,0x2F,0x30,0x31,0x32,0x33,0x34,0x35, - 0x56 /* the 102nd key (actually to the right of l-shift) */ -}; - -static const WORD main_key_scan_abnt_qwerty[MAIN_LEN] = -{ - /* ` 1 2 3 4 5 6 7 8 9 0 - = */ - 0x29,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0A,0x0B,0x0C,0x0D, - /* q w e r t y u i o p [ ] */ - 0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1A,0x1B, - /* a s d f g h j k l ; ' \ */ - 0x1E,0x1F,0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28,0x2B, - /* \ z x c v b n m , . / */ - 0x5e,0x2C,0x2D,0x2E,0x2F,0x30,0x31,0x32,0x33,0x34,0x35, - 0x56, /* the 102nd key (actually to the right of l-shift) */ -}; - -static const WORD main_key_scan_qwerty_jp106[MAIN_LEN] = -{ - /* 1 2 3 4 5 6 7 8 9 0 - ^ \ (Yen) */ - 0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0A,0x0B,0x0C,0x0D,0x7D, - /* q w e r t y u i o p @ [ */ - 0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1A,0x1B, - /* a s d f g h j k l ; : ] */ - 0x1E,0x1F,0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28,0x2B, - /* z x c v b n m , . / \ (Underscore) */ - 0x2C,0x2D,0x2E,0x2F,0x30,0x31,0x32,0x33,0x34,0x35,0x73 -}; - - static const WORD main_key_vkey_qwerty[MAIN_LEN] = { /* NOTE: this layout must concur with the scan codes layout above */ @@ -985,76 +1052,74 @@ static const struct { in the appropriate dlls/kernel/nls/.nls file */ const char *comment; const char (*key)[MAIN_LEN][4]; - const WORD (*scan)[MAIN_LEN]; /* scan codes mapping */ const WORD (*vkey)[MAIN_LEN]; /* virtual key codes mapping */ } main_key_tab[]={ - {0x0409, "United States keyboard layout", &main_key_US, &main_key_scan_qwerty, &main_key_vkey_qwerty}, - {0x0409, "United States keyboard layout (phantom key version)", &main_key_US_phantom, &main_key_scan_qwerty, &main_key_vkey_qwerty}, - /* Dvorak users tend to run QWERTY keyboards and rely on Windows/X11/Wayland to translate to the correct keysyms */ - {0x0409, "United States keyboard layout (dvorak)", &main_key_US_dvorak, &main_key_scan_qwerty, &main_key_vkey_dvorak}, - {0x0409, "United States keyboard layout (programmer dvorak)", &main_key_US_programmer_dvorak, &main_key_scan_qwerty, &main_key_vkey_dvorak}, - {0x0409, "United States keyboard layout (dvorak with phantom key)", &main_key_US_dvorak_phantom, &main_key_scan_qwerty, &main_key_vkey_dvorak}, - {0x0409, "United States International keyboard layout", &main_key_US_intl, &main_key_scan_qwerty, &main_key_vkey_qwerty}, - {0x0809, "British keyboard layout", &main_key_UK, &main_key_scan_qwerty, &main_key_vkey_qwerty}, - {0x0407, "German keyboard layout", &main_key_DE, &main_key_scan_qwerty, &main_key_vkey_qwertz}, - {0x0807, "Swiss German keyboard layout", &main_key_SG, &main_key_scan_qwerty, &main_key_vkey_qwertz}, - {0x100c, "Swiss French keyboard layout", &main_key_SF, &main_key_scan_qwerty, &main_key_vkey_qwertz}, - {0x041d, "Swedish keyboard layout", &main_key_SE, &main_key_scan_qwerty, &main_key_vkey_qwerty_v2}, - {0x0425, "Estonian keyboard layout", &main_key_ET, &main_key_scan_qwerty, &main_key_vkey_qwerty}, - {0x0414, "Norwegian keyboard layout", &main_key_NO, &main_key_scan_qwerty, &main_key_vkey_qwerty}, - {0x0406, "Danish keyboard layout", &main_key_DA, &main_key_scan_qwerty, &main_key_vkey_qwerty}, - {0x040c, "French keyboard layout", &main_key_FR, &main_key_scan_qwerty, &main_key_vkey_azerty}, - {0x0c0c, "Canadian French keyboard layout", &main_key_CF, &main_key_scan_qwerty, &main_key_vkey_qwerty}, - {0x0c0c, "Canadian French keyboard layout (CA_fr)", &main_key_CA_fr, &main_key_scan_qwerty, &main_key_vkey_qwerty}, - {0x0c0c, "Canadian keyboard layout", &main_key_CA, &main_key_scan_qwerty, &main_key_vkey_qwerty}, - {0x080c, "Belgian keyboard layout", &main_key_BE, &main_key_scan_qwerty, &main_key_vkey_azerty}, - {0x0816, "Portuguese keyboard layout", &main_key_PT, &main_key_scan_qwerty, &main_key_vkey_qwerty}, - {0x0416, "Brazilian ABNT-2 keyboard layout", &main_key_PT_br, &main_key_scan_abnt_qwerty, &main_key_vkey_abnt_qwerty}, - {0x0416, "Brazilian ABNT-2 keyboard layout ALT GR", &main_key_PT_br_alt_gr,&main_key_scan_abnt_qwerty, &main_key_vkey_abnt_qwerty}, - {0x040b, "Finnish keyboard layout", &main_key_FI, &main_key_scan_qwerty, &main_key_vkey_qwerty}, - {0x0402, "Bulgarian bds keyboard layout", &main_key_BG_bds, &main_key_scan_qwerty, &main_key_vkey_qwerty}, - {0x0402, "Bulgarian phonetic keyboard layout", &main_key_BG_phonetic, &main_key_scan_qwerty, &main_key_vkey_qwerty}, - {0x0423, "Belarusian keyboard layout", &main_key_BY, &main_key_scan_qwerty, &main_key_vkey_qwerty}, - {0x0419, "Russian keyboard layout", &main_key_RU, &main_key_scan_qwerty, &main_key_vkey_qwerty}, - {0x0419, "Russian keyboard layout (phantom key version)", &main_key_RU_phantom, &main_key_scan_qwerty, &main_key_vkey_qwerty}, - {0x0419, "Russian keyboard layout KOI8-R", &main_key_RU_koi8r, &main_key_scan_qwerty, &main_key_vkey_qwerty}, - {0x0419, "Russian keyboard layout cp1251", &main_key_RU_cp1251, &main_key_scan_qwerty, &main_key_vkey_qwerty}, - {0x0419, "Russian phonetic keyboard layout", &main_key_RU_phonetic, &main_key_scan_qwerty, &main_key_vkey_qwerty}, - {0x0422, "Ukrainian keyboard layout KOI8-U", &main_key_UA, &main_key_scan_qwerty, &main_key_vkey_qwerty}, - {0x0422, "Ukrainian keyboard layout (standard)", &main_key_UA_std, &main_key_scan_qwerty, &main_key_vkey_qwerty}, - {0x0419, "Russian keyboard layout (standard)", &main_key_RU_std, &main_key_scan_qwerty, &main_key_vkey_qwerty}, - {0x040a, "Spanish keyboard layout", &main_key_ES, &main_key_scan_qwerty, &main_key_vkey_qwerty}, - {0x0410, "Italian keyboard layout", &main_key_IT, &main_key_scan_qwerty, &main_key_vkey_qwerty}, - {0x040f, "Icelandic keyboard layout", &main_key_IS, &main_key_scan_qwerty, &main_key_vkey_qwerty}, - {0x040e, "Hungarian keyboard layout", &main_key_HU, &main_key_scan_qwerty, &main_key_vkey_qwertz}, - {0x0415, "Polish (programmer's) keyboard layout", &main_key_PL, &main_key_scan_qwerty, &main_key_vkey_qwerty}, - {0x0424, "Slovenian keyboard layout", &main_key_SI, &main_key_scan_qwerty, &main_key_vkey_qwertz}, - {0x0c1a, "Serbian keyboard layout sr", &main_key_SR, &main_key_scan_qwerty, &main_key_vkey_qwerty}, /* LANG_SERBIAN,SUBLANG_SERBIAN_CYRILLIC */ - {0x0c1a, "Serbian keyboard layout us,sr", &main_key_US_SR, &main_key_scan_qwerty, &main_key_vkey_qwerty}, /* LANG_SERBIAN,SUBLANG_SERBIAN_CYRILLIC */ - {0x041a, "Croatian keyboard layout", &main_key_HR, &main_key_scan_qwerty, &main_key_vkey_qwertz}, - {0x041a, "Croatian keyboard layout (specific)", &main_key_HR_jelly, &main_key_scan_qwerty, &main_key_vkey_qwerty}, - {0x0411, "Japanese 106 keyboard layout", &main_key_JA_jp106, &main_key_scan_qwerty_jp106, &main_key_vkey_qwerty_jp106}, - {0x0411, "Japanese Mac keyboard layout", &main_key_JA_macjp, &main_key_scan_qwerty_jp106, &main_key_vkey_qwerty_jp106}, - {0x0411, "Japanese pc98x1 keyboard layout", &main_key_JA_pc98x1, &main_key_scan_qwerty, &main_key_vkey_qwerty}, - {0x041b, "Slovak keyboard layout", &main_key_SK, &main_key_scan_qwerty, &main_key_vkey_qwerty}, - {0x041b, "Slovak and Czech keyboard layout without dead keys", &main_key_SK_prog, &main_key_scan_qwerty, &main_key_vkey_qwerty}, - {0x0405, "Czech keyboard layout", &main_key_CS, &main_key_scan_qwerty, &main_key_vkey_qwerty}, - {0x0405, "Czech keyboard layout cz", &main_key_CZ, &main_key_scan_qwerty, &main_key_vkey_qwertz}, - {0x0405, "Czech keyboard layout cz_qwerty", &main_key_CZ_qwerty, &main_key_scan_qwerty, &main_key_vkey_qwerty}, - {0x040a, "Latin American keyboard layout", &main_key_LA, &main_key_scan_qwerty, &main_key_vkey_qwerty}, - {0x0427, "Lithuanian (Baltic) keyboard layout", &main_key_LT_B, &main_key_scan_qwerty, &main_key_vkey_qwerty}, - {0x041f, "Turkish keyboard layout", &main_key_TK, &main_key_scan_qwerty, &main_key_vkey_qwerty}, - {0x041f, "Turkish keyboard layout tr", &main_key_TR, &main_key_scan_qwerty, &main_key_vkey_qwerty}, - {0x041f, "Turkish keyboard layout trf", &main_key_TR_F, &main_key_scan_qwerty, &main_key_vkey_qwerty}, - {0x040d, "Israelian keyboard layout", &main_key_IL, &main_key_scan_qwerty, &main_key_vkey_qwerty}, - {0x040d, "Israelian phonetic keyboard layout", &main_key_IL_phonetic, &main_key_scan_qwerty, &main_key_vkey_qwerty}, - {0x040d, "Israelian Saharon keyboard layout", &main_key_IL_saharon, &main_key_scan_qwerty, &main_key_vkey_qwerty}, - {0x0409, "VNC keyboard layout", &main_key_vnc, &main_key_scan_vnc, &main_key_vkey_vnc}, - {0x0408, "Greek keyboard layout", &main_key_EL, &main_key_scan_qwerty, &main_key_vkey_qwerty}, - {0x041e, "Thai (Kedmanee) keyboard layout", &main_key_th, &main_key_scan_qwerty, &main_key_vkey_qwerty}, - {0x0413, "Dutch keyboard layout", &main_key_NL, &main_key_scan_qwerty, &main_key_vkey_qwerty}, - - {0, NULL, NULL, NULL, NULL} /* sentinel */ + {0x0409, "United States keyboard layout", &main_key_US, &main_key_vkey_qwerty}, + {0x0409, "United States keyboard layout (phantom key version)", &main_key_US_phantom, &main_key_vkey_qwerty}, + {0x0409, "United States keyboard layout (dvorak)", &main_key_US_dvorak, &main_key_vkey_dvorak}, + {0x0409, "United States keyboard layout (programmer dvorak)", &main_key_US_programmer_dvorak, &main_key_vkey_dvorak}, + {0x0409, "United States keyboard layout (dvorak with phantom key)", &main_key_US_dvorak_phantom, &main_key_vkey_dvorak}, + {0x0409, "United States International keyboard layout", &main_key_US_intl, &main_key_vkey_qwerty}, + {0x0809, "British keyboard layout", &main_key_UK, &main_key_vkey_qwerty}, + {0x0407, "German keyboard layout", &main_key_DE, &main_key_vkey_qwertz}, + {0x0807, "Swiss German keyboard layout", &main_key_SG, &main_key_vkey_qwertz}, + {0x100c, "Swiss French keyboard layout", &main_key_SF, &main_key_vkey_qwertz}, + {0x041d, "Swedish keyboard layout", &main_key_SE, &main_key_vkey_qwerty_v2}, + {0x0425, "Estonian keyboard layout", &main_key_ET, &main_key_vkey_qwerty}, + {0x0414, "Norwegian keyboard layout", &main_key_NO, &main_key_vkey_qwerty}, + {0x0406, "Danish keyboard layout", &main_key_DA, &main_key_vkey_qwerty}, + {0x040c, "French keyboard layout", &main_key_FR, &main_key_vkey_azerty}, + {0x0c0c, "Canadian French keyboard layout", &main_key_CF, &main_key_vkey_qwerty}, + {0x0c0c, "Canadian French keyboard layout (CA_fr)", &main_key_CA_fr, &main_key_vkey_qwerty}, + {0x0c0c, "Canadian keyboard layout", &main_key_CA, &main_key_vkey_qwerty}, + {0x080c, "Belgian keyboard layout", &main_key_BE, &main_key_vkey_azerty}, + {0x0816, "Portuguese keyboard layout", &main_key_PT, &main_key_vkey_qwerty}, + {0x0416, "Brazilian ABNT-2 keyboard layout", &main_key_PT_br, &main_key_vkey_abnt_qwerty}, + {0x0416, "Brazilian ABNT-2 keyboard layout ALT GR", &main_key_PT_br_alt_gr,&main_key_vkey_abnt_qwerty}, + {0x040b, "Finnish keyboard layout", &main_key_FI, &main_key_vkey_qwerty}, + {0x0402, "Bulgarian bds keyboard layout", &main_key_BG_bds, &main_key_vkey_qwerty}, + {0x0402, "Bulgarian phonetic keyboard layout", &main_key_BG_phonetic, &main_key_vkey_qwerty}, + {0x0423, "Belarusian keyboard layout", &main_key_BY, &main_key_vkey_qwerty}, + {0x0419, "Russian keyboard layout", &main_key_RU, &main_key_vkey_qwerty}, + {0x0419, "Russian keyboard layout (phantom key version)", &main_key_RU_phantom, &main_key_vkey_qwerty}, + {0x0419, "Russian keyboard layout KOI8-R", &main_key_RU_koi8r, &main_key_vkey_qwerty}, + {0x0419, "Russian keyboard layout cp1251", &main_key_RU_cp1251, &main_key_vkey_qwerty}, + {0x0419, "Russian phonetic keyboard layout", &main_key_RU_phonetic, &main_key_vkey_qwerty}, + {0x0422, "Ukrainian keyboard layout KOI8-U", &main_key_UA, &main_key_vkey_qwerty}, + {0x0422, "Ukrainian keyboard layout (standard)", &main_key_UA_std, &main_key_vkey_qwerty}, + {0x0419, "Russian keyboard layout (standard)", &main_key_RU_std, &main_key_vkey_qwerty}, + {0x040a, "Spanish keyboard layout", &main_key_ES, &main_key_vkey_qwerty}, + {0x0410, "Italian keyboard layout", &main_key_IT, &main_key_vkey_qwerty}, + {0x040f, "Icelandic keyboard layout", &main_key_IS, &main_key_vkey_qwerty}, + {0x040e, "Hungarian keyboard layout", &main_key_HU, &main_key_vkey_qwertz}, + {0x0415, "Polish (programmer's) keyboard layout", &main_key_PL, &main_key_vkey_qwerty}, + {0x0424, "Slovenian keyboard layout", &main_key_SI, &main_key_vkey_qwertz}, + {0x0c1a, "Serbian keyboard layout sr", &main_key_SR, &main_key_vkey_qwerty}, /* LANG_SERBIAN,SUBLANG_SERBIAN_CYRILLIC */ + {0x0c1a, "Serbian keyboard layout us,sr", &main_key_US_SR, &main_key_vkey_qwerty}, /* LANG_SERBIAN,SUBLANG_SERBIAN_CYRILLIC */ + {0x041a, "Croatian keyboard layout", &main_key_HR, &main_key_vkey_qwertz}, + {0x041a, "Croatian keyboard layout (specific)", &main_key_HR_jelly, &main_key_vkey_qwerty}, + {0x0411, "Japanese 106 keyboard layout", &main_key_JA_jp106, &main_key_vkey_qwerty_jp106}, + {0x0411, "Japanese Mac keyboard layout", &main_key_JA_macjp, &main_key_vkey_qwerty_jp106}, + {0x0411, "Japanese pc98x1 keyboard layout", &main_key_JA_pc98x1, &main_key_vkey_qwerty}, + {0x041b, "Slovak keyboard layout", &main_key_SK, &main_key_vkey_qwerty}, + {0x041b, "Slovak and Czech keyboard layout without dead keys", &main_key_SK_prog, &main_key_vkey_qwerty}, + {0x0405, "Czech keyboard layout", &main_key_CS, &main_key_vkey_qwerty}, + {0x0405, "Czech keyboard layout cz", &main_key_CZ, &main_key_vkey_qwertz}, + {0x0405, "Czech keyboard layout cz_qwerty", &main_key_CZ_qwerty, &main_key_vkey_qwerty}, + {0x040a, "Latin American keyboard layout", &main_key_LA, &main_key_vkey_qwerty}, + {0x0427, "Lithuanian (Baltic) keyboard layout", &main_key_LT_B, &main_key_vkey_qwerty}, + {0x041f, "Turkish keyboard layout", &main_key_TK, &main_key_vkey_qwerty}, + {0x041f, "Turkish keyboard layout tr", &main_key_TR, &main_key_vkey_qwerty}, + {0x041f, "Turkish keyboard layout trf", &main_key_TR_F, &main_key_vkey_qwerty}, + {0x040d, "Israelian keyboard layout", &main_key_IL, &main_key_vkey_qwerty}, + {0x040d, "Israelian phonetic keyboard layout", &main_key_IL_phonetic, &main_key_vkey_qwerty}, + {0x040d, "Israelian Saharon keyboard layout", &main_key_IL_saharon, &main_key_vkey_qwerty}, + {0x0409, "VNC keyboard layout", &main_key_vnc, &main_key_vkey_vnc}, + {0x0408, "Greek keyboard layout", &main_key_EL, &main_key_vkey_qwerty}, + {0x041e, "Thai (Kedmanee) keyboard layout", &main_key_th, &main_key_vkey_qwerty}, + {0x0413, "Dutch keyboard layout", &main_key_NL, &main_key_vkey_qwerty}, + + {0, NULL, NULL, NULL} /* sentinel */ }; static unsigned kbd_layout=0; /* index into above table of layouts */ #ifdef SONAME_LIBXKBREGISTRY @@ -1326,7 +1391,7 @@ BOOL X11DRV_KeymapNotify( HWND hwnd, XEvent *event ) } keys[256]; struct x11drv_thread_data *thread_data = x11drv_thread_data(); const struct layout *layout = thread_data->layout; - const WORD *scancodes = layout->keyc2scan; + const WORD *scancodes = get_layout_fuzzy_scancodes( layout ); keymapnotify_hwnd = thread_data->keymapnotify_hwnd; thread_data->keymapnotify_hwnd = NULL; @@ -1472,7 +1537,7 @@ BOOL X11DRV_KeyEvent( HWND hwnd, XEvent *xev ) { struct x11drv_thread_data *thread_data = x11drv_thread_data(); const struct layout *layout = thread_data->layout; - const WORD *scancodes = layout->keyc2scan; + const WORD *scancodes = get_layout_fuzzy_scancodes( layout ); XKeyEvent *event = &xev->xkey; char buf[24]; char *Str = buf; @@ -1731,7 +1796,7 @@ static DWORD klid_from_xkb_layout( const char *layout, const char *variant ) struct klid_map_entry key = { layout, variant }; const struct klid_map_entry *entry; - if (!variant) + if (!variant || !strcmp(variant, "vnc")) return 0; entry = bsearch( &key, klid_map, ARRAY_SIZE(klid_map), sizeof(*klid_map), klid_map_cmp ); @@ -1864,7 +1929,7 @@ static unsigned int detect_keyboard_layout( Display *display, XModifierKeymap *m return best_layout; } -static void init_scancodes_table( Display *display, WORD scancodes[256], unsigned int xkb_group ) +static BOOL init_scancodes_table( Display *display, WORD scancodes[256], unsigned int xkb_group ) { KeySym keysym; XKeyEvent e2; @@ -1875,6 +1940,8 @@ static void init_scancodes_table( Display *display, WORD scancodes[256], unsigne XModifierKeymap *modmap; unsigned int state, mod, dummy, altgr_mod = 0; + WARN("nonstandard keycodes to keysym mapping, using fuzzy scancode mapping\n"); + modmap = XGetModifierMapping( display ); for (mod = 0; mod < 8 * modmap->max_keypermod; mod++) { @@ -1887,7 +1954,7 @@ static void init_scancodes_table( Display *display, WORD scancodes[256], unsigne TRACE( "AltGr is mapped to mod %#x\n", altgr_mod ); XFreeModifiermap( modmap ); - lkey = main_key_tab[kbd_layout].key; + lkey = &main_key_vnc; syms = (keysyms_per_keycode > 4) ? 4 : keysyms_per_keycode; e2.display = display; @@ -1958,7 +2025,7 @@ static void init_scancodes_table( Display *display, WORD scancodes[256], unsigne if (maxval >= 0) { /* got it */ - const WORD (*lscan)[MAIN_LEN] = main_key_tab[kbd_layout].scan; + const WORD (*lscan)[MAIN_LEN] = &main_key_scan_vnc; scan = (*lscan)[maxval]; } } @@ -1987,14 +2054,15 @@ static void init_scancodes_table( Display *display, WORD scancodes[256], unsigne TRACE_(key)("assigning scancode %02x to unidentified keycode %u (%s)\n", scan, keyc, ksname); scancodes[keyc] = scan++; } + return TRUE; } -/* initialize keyc2scan and keyc2vkey */ +/* initialize keyc2vkey */ static void init_keycode_mappings( Display *display ) { KeySym keysym; XKeyEvent e2; - WORD scan, vkey; + WORD vkey; int keyc, i, keyn, syms; char ckey[4]={0,0,0,0}; const char (*lkey)[MAIN_LEN][4]; @@ -2039,23 +2107,18 @@ static void init_keycode_mappings( Display *display ) keysym = 0; e2.keycode = (KeyCode)keyc; have_chars = XLookupString(&e2, buf, sizeof(buf), &keysym, NULL); - vkey = 0; scan = 0; + vkey = 0; if (keysym) /* otherwise, keycode not used */ { if ((keysym >> 8) == 0xFF) /* non-character key */ { vkey = nonchar_key_vkey[keysym & 0xff]; - scan = nonchar_key_scan[keysym & 0xff]; - /* set extended bit when necessary */ - if (scan & 0x100) vkey |= 0x100; } else if ((keysym >> 8) == 0x1008FF) { /* XFree86 vendor keys */ vkey = xfree86_vendor_key_vkey[keysym & 0xff]; /* All vendor keys are extended with a scan code of 0 per testing on WinXP */ - scan = 0x100; vkey |= 0x100; } else if (keysym == 0x20) { /* Spacebar */ vkey = VK_SPACE; - scan = 0x39; } else if (have_chars) { /* we seem to need to search the layout-dependent scancodes */ int maxlen=0,maxval=-1,ok; @@ -2087,9 +2150,7 @@ static void init_keycode_mappings( Display *display ) } if (maxval>=0) { /* got it */ - const WORD (*lscan)[MAIN_LEN] = main_key_tab[kbd_layout].scan; const WORD (*lvkey)[MAIN_LEN] = main_key_tab[kbd_layout].vkey; - scan = (*lscan)[maxval]; vkey = (*lvkey)[maxval]; } } @@ -2237,6 +2298,7 @@ static void find_xkb_layout_variant( Display *display, XModifierKeymap *mmp, int index = detect_keyboard_layout( display, mmp, group ); *layout = xkb_layout_from_langid( main_key_tab[index].lcid ); + if (strstr( main_key_tab[index].comment, "VNC" )) *variant = "vnc"; if (strstr( main_key_tab[index].comment, "dvorak" )) *variant = "dvorak"; } @@ -2517,7 +2579,7 @@ UINT X11DRV_MapVirtualKeyEx( UINT wCode, UINT wMapType, HKL hkl ) { struct x11drv_thread_data *thread_data = x11drv_thread_data(); const struct layout *layout = thread_data->layout; - const WORD *scancodes = layout->keyc2scan; + const WORD *scancodes = get_layout_fuzzy_scancodes( layout ); UINT ret = 0; int keyc; Display *display = thread_init_display(); @@ -2662,7 +2724,7 @@ INT X11DRV_GetKeyNameText( LONG lParam, LPWSTR lpBuffer, INT nSize ) { struct x11drv_thread_data *thread_data = x11drv_thread_data(); const struct layout *layout = thread_data->layout; - const WORD *scancodes = layout->keyc2scan; + const WORD *scancodes = get_layout_fuzzy_scancodes( layout ); Display *display = thread_init_display(); int vkey, ansi, scanCode; KeyCode keyc; @@ -2672,6 +2734,7 @@ INT X11DRV_GetKeyNameText( LONG lParam, LPWSTR lpBuffer, INT nSize ) scanCode = lParam >> 16; scanCode &= 0x1ff; /* keep "extended-key" flag with code */ + if (scanCode == 0x145) return -1; /* use default implementation for VK_PAUSE */ vkey = X11DRV_MapVirtualKeyEx( scanCode, MAPVK_VSC_TO_VK_EX, NtUserGetKeyboardLayout(0) ); -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10963
From: Matteo Bruni <mbruni@codeweavers.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 6ad01e82978..9c9b83e1578 100644 --- a/dlls/user32/tests/input.c +++ b/dlls/user32/tests/input.c @@ -1313,6 +1313,27 @@ static void test_SendInput_keyboard_messages( WORD vkey, WORD scan, WCHAR wch, W {0}, }; + struct send_input_keyboard_test pause_scan[] = + { + {.scan = 0x21d, .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 = 0x21d, .flags = KEYEVENTF_SCANCODE | KEYEVENTF_KEYUP, .expect_state = {[VK_CONTROL] = 0x01, [VK_LCONTROL] = 0x01}, + .expect = {KEY_HOOK(WM_KEYUP, 0x1d, VK_LCONTROL), KEY_MSG(WM_KEYUP, 0x1d, VK_CONTROL), {0}}}, + {.scan = 0xe11d, .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 = 0xe11d, .flags = KEYEVENTF_SCANCODE | KEYEVENTF_KEYUP, .expect_state = {[VK_CONTROL] = 0x01, [VK_LCONTROL] = 0x01}, + .expect = {KEY_HOOK(WM_KEYUP, 0x1d, VK_LCONTROL), KEY_MSG(WM_KEYUP, 0x1d, VK_CONTROL), {0}}}, + {.scan = 0xe11d, .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 = 0xe11d, .flags = KEYEVENTF_SCANCODE | KEYEVENTF_EXTENDEDKEY | KEYEVENTF_KEYUP, .expect_state = {[VK_CONTROL] = 0x01, [VK_RCONTROL] = 0x01}, + .expect = {KEY_HOOK_(WM_KEYUP, 0x1d, VK_RCONTROL, LLKHF_EXTENDED, .todo_value = TRUE), KEY_MSG(WM_KEYUP, 0x11d, VK_CONTROL), {0}}}, + {.vkey = VK_PAUSE, .expect_state = {[VK_PAUSE] = 0x80}, + .expect = {KEY_HOOK(WM_KEYDOWN, 0x7, VK_PAUSE), KEY_MSG(WM_KEYDOWN, 0x7, VK_PAUSE), {0}}}, + {.vkey = VK_PAUSE, .flags = KEYEVENTF_KEYUP, .expect_state = {[VK_PAUSE] = 0x01}, + .expect = {KEY_HOOK(WM_KEYUP, 0x8, VK_PAUSE), KEY_MSG(WM_KEYUP, 0x8, VK_PAUSE), {0}}}, + {0}, + }; + #undef WIN_MSG #undef KBD_HOOK #undef KEY_HOOK_ @@ -1397,6 +1418,7 @@ static void test_SendInput_keyboard_messages( WORD vkey, WORD scan, WCHAR wch, W 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 ); + check_send_input_keyboard_test( pause_scan, TRUE ); winetest_pop_context(); wait_messages( 100, FALSE ); @@ -1444,6 +1466,7 @@ static void test_SendInput_keyboard_messages( WORD vkey, WORD scan, WCHAR wch, W 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 ); + check_send_input_keyboard_test( pause_scan, FALSE ); winetest_pop_context(); ok_ret( 1, DestroyWindow( hwnd ) ); -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10963
From: Matteo Bruni <mbruni@codeweavers.com> --- dlls/user32/tests/input.c | 42 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/dlls/user32/tests/input.c b/dlls/user32/tests/input.c index 9c9b83e1578..5120f43fff7 100644 --- a/dlls/user32/tests/input.c +++ b/dlls/user32/tests/input.c @@ -1479,6 +1479,36 @@ static void test_SendInput_keyboard_messages( WORD vkey, WORD scan, WCHAR wch, W static void test_keynames(void) { + static const struct + { + LONG lparam; + const char *expected_name; + BOOL todo; + BOOL todo_value; + } tests[] = + { + {0x00370000, "Num *", .todo_value = TRUE}, + {0x01370000, "Prnt Scrn", .todo_value = TRUE}, + {0x02370000, "Num *", .todo_value = TRUE}, + {0xe0370000, "Num *", .todo_value = TRUE}, + {0xe1370000, "Prnt Scrn", .todo_value = TRUE}, + {0x00450000, "Pause", .todo_value = TRUE}, + {0x01450000, "Num Lock"}, + {0x02450000, "Pause", .todo_value = TRUE}, + {0xe0450000, "Pause", .todo_value = TRUE}, + {0xe1450000, "Num Lock"}, + {0x00460000, "Scroll Lock", .todo_value = TRUE}, + {0x01460000, "Break", .todo_value = TRUE}, + {0xe0460000, "Scroll Lock", .todo_value = TRUE}, + {0xe1460000, "Break", .todo_value = TRUE}, + {0x01480000, "Up"}, + {0x001d0000, "Ctrl", .todo_value = TRUE}, + {0x011d0000, "Right Ctrl", .todo_value = TRUE}, + {0x021d0000, "Ctrl", .todo_value = TRUE}, + {0xe01d0000, "Ctrl", .todo_value = TRUE}, + {0xe11d0000, "Right Ctrl", .todo_value = TRUE}, + }; + BOOL us_kbd = (GetKeyboardLayout(0) == (HKL)(ULONG_PTR)0x04090409); int i, len; char buff[256]; @@ -1488,6 +1518,18 @@ static void test_keynames(void) len = GetKeyNameTextA(i << 16, buff, sizeof(buff)); ok(len || !buff[0], "%d: Buffer is not zeroed\n", i); } + + if (!us_kbd) skip("skipping test with inconsistent results on non-us keyboard\n"); + else for (i = 0; i < ARRAY_SIZE(tests); i++) + { + winetest_push_context("scancode %04x", (unsigned int)tests[i].lparam >> 16); + len = GetKeyNameTextA(tests[i].lparam, buff, sizeof(buff)); + todo_wine_if(tests[i].todo) ok(len, "No key name\n"); + todo_wine_if(tests[i].todo_value) + ok(!strcmp(buff, tests[i].expected_name), "Unexpected key name %s\n", debugstr_a(buff)); + trace("name %s\n", debugstr_a(buff)); + winetest_pop_context(); + } } static BOOL accept_keyboard_messages_raw( UINT msg ) -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10963
From: Matteo Bruni <mbruni@codeweavers.com> --- dlls/user32/tests/input.c | 64 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 63 insertions(+), 1 deletion(-) diff --git a/dlls/user32/tests/input.c b/dlls/user32/tests/input.c index 5120f43fff7..922e300c1fd 100644 --- a/dlls/user32/tests/input.c +++ b/dlls/user32/tests/input.c @@ -3261,10 +3261,65 @@ static void test_DefRawInputProc(void) ok(GetLastError() == 0xdeadbeef, "got %ld\n", GetLastError()); } +static const char *debug_map_type(UINT map_type) +{ +#define MAP_TO_STR(x) case x: return #x + switch (map_type) + { + MAP_TO_STR(MAPVK_VK_TO_VSC); + MAP_TO_STR(MAPVK_VSC_TO_VK); + MAP_TO_STR(MAPVK_VK_TO_CHAR); + MAP_TO_STR(MAPVK_VSC_TO_VK_EX); + MAP_TO_STR(MAPVK_VK_TO_VSC_EX); + default: + return "<unknown>"; + } +#undef MAP_TO_STR +} + static void test_key_map(void) { + static const struct + { + UINT input; + UINT map_type; + UINT expected; + BOOL todo; + } tests[] = + { + {0x136, MAPVK_VSC_TO_VK_EX, 0, TRUE}, + {0xe036, MAPVK_VSC_TO_VK_EX, 0, TRUE}, + {0xe136, MAPVK_VSC_TO_VK_EX, 0, TRUE}, + {0x37, MAPVK_VSC_TO_VK_EX, VK_MULTIPLY, TRUE}, + {0x137, MAPVK_VSC_TO_VK_EX, 0, TRUE}, + {0x237, MAPVK_VSC_TO_VK_EX, 0, TRUE}, + {0xe037, MAPVK_VSC_TO_VK_EX, VK_SNAPSHOT}, + {0xe137, MAPVK_VSC_TO_VK_EX, 0, TRUE}, + {0x45, MAPVK_VSC_TO_VK_EX, VK_NUMLOCK}, + {0x145, MAPVK_VSC_TO_VK_EX, 0, TRUE}, + {0xe045, MAPVK_VSC_TO_VK_EX, 0, TRUE}, + {0xe145, MAPVK_VSC_TO_VK_EX, 0, TRUE}, + {0x1d, MAPVK_VSC_TO_VK_EX, VK_LCONTROL}, + {0x011d, MAPVK_VSC_TO_VK_EX, 0, TRUE}, + {0x021d, MAPVK_VSC_TO_VK_EX, 0, TRUE}, + {0xe01d, MAPVK_VSC_TO_VK_EX, VK_RCONTROL, TRUE}, + {0xe11d, MAPVK_VSC_TO_VK_EX, VK_PAUSE, TRUE}, + {0x46, MAPVK_VSC_TO_VK_EX, VK_SCROLL}, + {0xe046, MAPVK_VSC_TO_VK_EX, VK_CANCEL, TRUE}, + + {VK_RSHIFT, MAPVK_VK_TO_VSC_EX, 0x36}, + {VK_MULTIPLY, MAPVK_VK_TO_VSC_EX, 0x37}, + {VK_NUMLOCK, MAPVK_VK_TO_VSC_EX, 0x45}, + {VK_UP, MAPVK_VK_TO_VSC_EX, 0x48}, + {VK_LCONTROL, MAPVK_VK_TO_VSC_EX, 0x1d}, + {VK_RCONTROL, MAPVK_VK_TO_VSC_EX, 0xe01d}, + {VK_PAUSE, MAPVK_VK_TO_VSC_EX, 0xe11d, TRUE}, + {VK_SCROLL, MAPVK_VK_TO_VSC_EX, 0x46}, + {VK_CANCEL, MAPVK_VK_TO_VSC_EX, 0xe046, TRUE}, + {VK_SNAPSHOT, MAPVK_VK_TO_VSC_EX, 0x54}, + }; HKL kl = GetKeyboardLayout(0); - UINT kL, kR, s, sL; + UINT kL, kR, s, sL, r; int i; static const UINT numpad_collisions[][2] = { { VK_NUMPAD0, VK_INSERT }, @@ -3321,6 +3376,13 @@ static void test_key_map(void) ok(s >> 8 == 0xE0 || broken(s == 0), "Scan code prefix for VK_RMENU should be 0xE0 when MAPVK_VK_TO_VSC_EX is set, was %#1x\n", s >> 8); s = MapVirtualKeyExA(VK_RSHIFT, MAPVK_VK_TO_VSC_EX, kl); ok(s >> 8 == 0x00 || broken(s == 0), "The scan code shouldn't have a prefix, got %#1x\n", s >> 8); + + for (i = 0; i < ARRAY_SIZE(tests); i++) + { + r = MapVirtualKeyExA(tests[i].input, tests[i].map_type, kl); + todo_wine_if(tests[i].todo) ok(r == tests[i].expected, "Unexpected %s %x -> %x\n", + debug_map_type(tests[i].map_type), tests[i].input, r); + } } #define shift 1 -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10963
Pushed a new version that takes care of TightVNC's era Xvnc keycode -> scancode mapping. There's probably room for further cleanup and simplification, but I thought it might be better to show where I'm currently at early rather than late. Basically I'm keeping only the old vnc mapping tables (the other ones we had are effectively an approximation of the fixed mapping we're using starting from 863836da) with some limited dynamic lookup for "character" keys. FWIW, the mapping I'm getting seems to match https://github.com/TurboVNC/tightvnc/blob/main/vnc_unixsrc/Xvnc/programs/Xse... and I guess we could consider using an alternate static mapping function when detecting this. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/10963#note_143540
On Fri Jun 19 10:03:12 2026 +0000, Matteo Bruni wrote:
Pushed a new version that takes care of TightVNC's era Xvnc keycode -> scancode mapping. There's probably room for further cleanup and simplification, but I thought it might be better to show where I'm currently at early rather than late. Basically I'm keeping only the old vnc mapping tables (the other ones we had are effectively an approximation of the fixed mapping we're using starting from 863836da) with some limited dynamic lookup for "character" keys. FWIW, the mapping I'm getting seems to match https://github.com/TurboVNC/tightvnc/blob/main/vnc_unixsrc/Xvnc/programs/Xse... and I guess we could consider using an alternate static mapping function when detecting this. Well I'm still not sure it's worth keeping code for such very specific use case, if tightvncserver is the only X server we've found that doesn't use evdev-compatible scancodes (for the low value range), and if it's been broken because of XShape errors and been like this for a while without anybody complaining.
Note that in addition to it being broken because of XShape, it seems that tightvncserver simply doesn't support XKB at all, and perhaps this is simply the source of our troubles. The Xkb xlib calls we do seem to mostly cope with the missing extension, but we could probably use that information to special case this scenario, if we really want to keep support for it. With the idea of getting rid of the fuzzy layout detection, it seems possible to treat all missing XKB support under a unique workaround. For instance a fallback keycode -> scancode mapping table like this seems to do the trick for me: ``` static WORD default_keyc2scan[0x100]; static BOOL use_xkb; static void init_default_keyc2scan( Display *display ) { static const WORD keysyms[] = { 0, XK_Escape, XK_1, XK_2, XK_3, XK_4, XK_5, XK_6, XK_7, XK_8, XK_9, XK_0, XK_minus, XK_equal, XK_BackSpace, XK_Tab, XK_q, XK_w, XK_e, XK_r, XK_t, XK_y, XK_u, XK_i, XK_o, XK_p, XK_braceleft, XK_braceright, XK_Return, XK_Control_L, XK_a, XK_s, XK_d, XK_f, XK_g, XK_h, XK_j, XK_k, XK_l, XK_semicolon, XK_apostrophe, XK_grave, XK_Shift_L, XK_backslash, XK_z, XK_x, XK_c, XK_v, XK_b, XK_n, XK_m, XK_comma, XK_period, XK_slash, XK_Shift_R, XK_asterisk, XK_Alt_L, XK_space, XK_Caps_Lock, XK_F1, XK_F2, XK_F3, XK_F4, XK_F5, XK_F6, XK_F7, XK_F8, XK_F9, XK_F10, XK_Num_Lock, XK_Scroll_Lock, XK_KP_7, XK_KP_8, XK_KP_9, XK_KP_Subtract, XK_KP_4, XK_KP_5, XK_KP_6, XK_KP_Add, XK_KP_1, XK_KP_2, XK_KP_3, XK_KP_0, XK_KP_Decimal, [0x5a] = XK_ISO_Level3_Shift, [KEY_RIGHTCTRL] = XK_Control_R, [KEY_LEFTMETA] = XK_Meta_L, [KEY_RIGHTMETA] = XK_Meta_R, [KEY_RIGHTALT] = XK_Alt_R, [KEY_HOME] = XK_Home, [KEY_END] = XK_End, [KEY_PAGEUP] = XK_Page_Up, [KEY_PAGEDOWN] = XK_Page_Down, [KEY_UP] = XK_Up, [KEY_DOWN] = XK_Down, [KEY_LEFT] = XK_Left, [KEY_RIGHT] = XK_Right, [KEY_F11] = XK_F11, [KEY_F12] = XK_F12, [KEY_DELETE] = XK_Delete, [KEY_KPENTER] = XK_KP_Enter, }; for (unsigned int keyc = min_keycode; keyc <= max_keycode; keyc++) { XKeyEvent event = { .display = display, .keycode = keyc }; unsigned int k; KeySym keysym; if (!(keysym = XLookupKeysym( &event, 0 ))) continue; for (k = 1; k < ARRAY_SIZE(keysyms); k++) if (keysyms[k] == keysym) break; if (k < ARRAY_SIZE(keysyms)) default_keyc2scan[keyc - min_keycode] = k; else WARN( "Failed to map keyc %#x keysym %#lx\n", keyc, keysym ); } } static WORD keyc2scan( unsigned int keycode, unsigned int state ) { unsigned int key = keycode - 8; if (!use_xkb) return default_keyc2scan[keycode - min_keycode]; /* ... */ /* in init_keyboard_layouts */ if (!use_xkb) init_default_keyc2scan( display ); /* ... */ /* in x11drv_init_keyboard */ use_xkb = XkbUseExtension( display, NULL, NULL ); ``` -- https://gitlab.winehq.org/wine/wine/-/merge_requests/10963#note_143597
On Fri Jun 19 10:03:12 2026 +0000, Rémi Bernon wrote:
Well I'm still not sure it's worth keeping code for such very specific use case, if tightvncserver is the only X server we've found that doesn't use evdev-compatible scancodes (for the low value range), and if it's been broken because of XShape errors and been like this for a while without anybody complaining. Note that in addition to it being broken because of XShape, it seems that tightvncserver simply doesn't support XKB at all, and perhaps this is simply the source of our troubles. The Xkb xlib calls we do seem to mostly cope with the missing extension, but we could probably use that information to special case this scenario, if we really want to keep support for it. With the idea of getting rid of the fuzzy layout detection, it seems possible to treat all missing XKB support under a unique workaround. For instance a fallback keycode -> scancode mapping table like this seems to do the trick for me: ``` static WORD default_keyc2scan[0x100]; static BOOL use_xkb; static void init_default_keyc2scan( Display *display ) { static const WORD keysyms[] = { 0, XK_Escape, XK_1, XK_2, XK_3, XK_4, XK_5, XK_6, XK_7, XK_8, XK_9, XK_0, XK_minus, XK_equal, XK_BackSpace, XK_Tab, XK_q, XK_w, XK_e, XK_r, XK_t, XK_y, XK_u, XK_i, XK_o, XK_p, XK_braceleft, XK_braceright, XK_Return, XK_Control_L, XK_a, XK_s, XK_d, XK_f, XK_g, XK_h, XK_j, XK_k, XK_l, XK_semicolon, XK_apostrophe, XK_grave, XK_Shift_L, XK_backslash, XK_z, XK_x, XK_c, XK_v, XK_b, XK_n, XK_m, XK_comma, XK_period, XK_slash, XK_Shift_R, XK_asterisk, XK_Alt_L, XK_space, XK_Caps_Lock, XK_F1, XK_F2, XK_F3, XK_F4, XK_F5, XK_F6, XK_F7, XK_F8, XK_F9, XK_F10, XK_Num_Lock, XK_Scroll_Lock, XK_KP_7, XK_KP_8, XK_KP_9, XK_KP_Subtract, XK_KP_4, XK_KP_5, XK_KP_6, XK_KP_Add, XK_KP_1, XK_KP_2, XK_KP_3, XK_KP_0, XK_KP_Decimal, [0x5a] = XK_ISO_Level3_Shift, [KEY_RIGHTCTRL] = XK_Control_R, [KEY_LEFTMETA] = XK_Meta_L, [KEY_RIGHTMETA] = XK_Meta_R, [KEY_RIGHTALT] = XK_Alt_R, [KEY_HOME] = XK_Home, [KEY_END] = XK_End, [KEY_PAGEUP] = XK_Page_Up, [KEY_PAGEDOWN] = XK_Page_Down, [KEY_UP] = XK_Up, [KEY_DOWN] = XK_Down, [KEY_LEFT] = XK_Left, [KEY_RIGHT] = XK_Right, [KEY_F11] = XK_F11, [KEY_F12] = XK_F12, [KEY_DELETE] = XK_Delete, [KEY_KPENTER] = XK_KP_Enter, }; for (unsigned int keyc = min_keycode; keyc <= max_keycode; keyc++) { XKeyEvent event = { .display = display, .keycode = keyc }; unsigned int k; KeySym keysym; if (!(keysym = XLookupKeysym( &event, 0 ))) continue; for (k = 1; k < ARRAY_SIZE(keysyms); k++) if (keysyms[k] == keysym) break; if (k < ARRAY_SIZE(keysyms)) default_keyc2scan[keyc - min_keycode] = k; else WARN( "Failed to map keyc %#x keysym %#lx\n", keyc, keysym ); } } static WORD keyc2scan( unsigned int keycode, unsigned int state ) { unsigned int key = keycode - 8; if (!use_xkb) return default_keyc2scan[keycode - min_keycode]; /* ... */ /* in init_keyboard_layouts */ if (!use_xkb) init_default_keyc2scan( display ); /* ... */ /* in x11drv_init_keyboard */ use_xkb = XkbUseExtension( display, NULL, NULL ); ``` Yes, that's what I meant with "room for further cleanup and simplification". I guess it depends on what we want to support exactly and where we draw the line. @julliard?
-- https://gitlab.winehq.org/wine/wine/-/merge_requests/10963#note_143607
participants (4)
-
Matteo Bruni -
Matteo Bruni (@Mystral) -
Rémi Bernon -
Rémi Bernon (@rbernon)