[PATCH v8 0/6] MR10963: winex11: Keyboard layouts rework, part 3
-- v8: 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. winewayland: Use a private header for evdev keycodes. winex11: Use scancode high bit to set KEYEVENTF_EXTENDEDKEY flag. 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 3d5d2c81f03..28d0f216907 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/winewayland.drv/wayland_keyboard.c | 2 +- include/Makefile.in | 1 + include/wine/evdev-keycodes.h | 106 ++++++++++++++++++++++++ 3 files changed, 108 insertions(+), 1 deletion(-) create mode 100644 include/wine/evdev-keycodes.h diff --git a/dlls/winewayland.drv/wayland_keyboard.c b/dlls/winewayland.drv/wayland_keyboard.c index 0a6eea2fa73..707f2cf0d61 100644 --- a/dlls/winewayland.drv/wayland_keyboard.c +++ b/dlls/winewayland.drv/wayland_keyboard.c @@ -26,7 +26,6 @@ #include "config.h" #include <stdlib.h> -#include <linux/input.h> #undef SW_MAX /* Also defined in winuser.rh */ #include <sys/mman.h> #include <unistd.h> @@ -34,6 +33,7 @@ #include "waylanddrv.h" #include "wine/debug.h" #include "wine/server.h" +#include "wine/evdev-keycodes.h" WINE_DEFAULT_DEBUG_CHANNEL(keyboard); WINE_DECLARE_DEBUG_CHANNEL(key); diff --git a/include/Makefile.in b/include/Makefile.in index 47df75fb59e..926acb307b2 100644 --- a/include/Makefile.in +++ b/include/Makefile.in @@ -1116,6 +1116,7 @@ SOURCES = \ wine/debug.h \ wine/dplaysp.h \ wine/epm.idl \ + wine/evdev-keycodes.h \ wine/exception.h \ wine/fil_data.idl \ wine/gdi_driver.h \ diff --git a/include/wine/evdev-keycodes.h b/include/wine/evdev-keycodes.h new file mode 100644 index 00000000000..785b55a231c --- /dev/null +++ b/include/wine/evdev-keycodes.h @@ -0,0 +1,106 @@ +/* + * evdev keycodes + * + * Copyright 2026 Matteo Bruni + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#ifndef __WINE_EVDEV_KEYCODES__ +#define __WINE_EVDEV_KEYCODES__ + +#define KEY_NUMLOCK 69 + +#define KEY_KPDOT 83 + +#define KEY_102ND 86 +#define KEY_F11 87 +#define KEY_F12 88 +#define KEY_KPENTER 96 +#define KEY_RIGHTCTRL 97 +#define KEY_KPSLASH 98 +#define KEY_SYSRQ 99 +#define KEY_RIGHTALT 100 +#define KEY_LINEFEED 101 +#define KEY_HOME 102 +#define KEY_UP 103 +#define KEY_PAGEUP 104 +#define KEY_LEFT 105 +#define KEY_RIGHT 106 +#define KEY_END 107 +#define KEY_DOWN 108 +#define KEY_PAGEDOWN 109 +#define KEY_INSERT 110 +#define KEY_DELETE 111 +#define KEY_MUTE 113 +#define KEY_VOLUMEDOWN 114 +#define KEY_VOLUMEUP 115 +#define KEY_POWER 116 +#define KEY_KPPLUSMINUS 118 +#define KEY_PAUSE 119 +#define KEY_SCALE 120 + +#define KEY_LEFTMETA 125 +#define KEY_RIGHTMETA 126 +#define KEY_COMPOSE 127 +#define KEY_STOP 128 + +#define KEY_OPEN 134 + +#define KEY_FIND 136 + +#define KEY_HELP 138 +#define KEY_MENU 139 + +#define KEY_SLEEP 142 + +#define KEY_PROG1 148 +#define KEY_PROG2 149 + +#define KEY_MAIL 155 +#define KEY_BOOKMARKS 156 +#define KEY_COMPUTER 157 +#define KEY_BACK 158 +#define KEY_FORWARD 159 + +#define KEY_NEXTSONG 163 +#define KEY_PLAYPAUSE 164 +#define KEY_PREVIOUSSONG 165 +#define KEY_STOPCD 166 + +#define KEY_HOMEPAGE 172 +#define KEY_REFRESH 173 +#define KEY_EXIT 174 + +#define KEY_F13 183 +#define KEY_F14 184 +#define KEY_F15 185 +#define KEY_F16 186 +#define KEY_F17 187 +#define KEY_F18 188 +#define KEY_F19 189 +#define KEY_F20 190 +#define KEY_F21 191 +#define KEY_F22 192 +#define KEY_F23 193 +#define KEY_F24 194 + +#define KEY_PRINT 210 + +#define KEY_CANCEL 223 + +#define KEY_MEDIA 226 + +#endif -- 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. --- dlls/dinput/tests/device8.c | 2 +- dlls/winex11.drv/keyboard.c | 374 +++++++++++++++++------------------- 2 files changed, 181 insertions(+), 195 deletions(-) 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 28d0f216907..035bab8d021 100644 --- a/dlls/winex11.drv/keyboard.c +++ b/dlls/winex11.drv/keyboard.c @@ -57,6 +57,8 @@ #include "wine/debug.h" #include "wine/list.h" +#include "wine/evdev-keycodes.h" + /* log format (add 0-padding as appropriate): keycode %u as in output from xev keysym %lx as in X11/keysymdef.h @@ -81,7 +83,94 @@ struct layout 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( UINT keycode ) +{ + UINT key = keycode - 8; + + /* base keys can be mapped directly */ + if (key <= KEY_KPDOT) return key; + + /* 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: return 0x021d; /* Y1D / VK_PAUSE */ + } + + /* otherwise just make up some extended scancode */ + return 0x200 | (key & 0x7f); +} static int NumLockMask, ScrollLockMask, AltGrMask; /* mask in the XKeyEvent state */ @@ -179,46 +268,6 @@ static void create_layout_from_xkb( int xkb_group, const char *xkb_layout, LANGI /* 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 */ @@ -936,13 +985,6 @@ static const char main_key_th[MAIN_LEN][4] = }; /*** VNC keyboard layout */ -static const WORD main_key_scan_vnc[MAIN_LEN] = -{ - 0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0A,0x0B,0x0C,0x0D,0x1A,0x1B,0x27,0x28,0x29,0x33,0x34,0x35,0x2B, - 0x1E,0x30,0x2E,0x20,0x12,0x21,0x22,0x23,0x17,0x24,0x25,0x26,0x32,0x31,0x18,0x19,0x10,0x13,0x1F,0x14,0x16,0x2F,0x11,0x2D,0x15,0x2C, - 0x56 -}; - static const WORD main_key_vkey_vnc[MAIN_LEN] = { '1','2','3','4','5','6','7','8','9','0',VK_OEM_MINUS,VK_OEM_PLUS,VK_OEM_4,VK_OEM_6,VK_OEM_1,VK_OEM_7,VK_OEM_3,VK_OEM_COMMA,VK_OEM_PERIOD,VK_OEM_2,VK_OEM_5, @@ -974,76 +1016,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 @@ -1127,53 +1167,6 @@ static const WORD nonchar_key_vkey[256] = 0, 0, 0, 0, 0, 0, 0, VK_DELETE /* FFF8 */ }; -static const WORD nonchar_key_scan[256] = -{ - /* unused */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* FF00 */ - /* special keys */ - 0x0E, 0x0F, 0x00, /*?*/ 0, 0x00, 0x1C, 0x00, 0x00, /* FF08 */ - 0x00, 0x00, 0x00, 0x45, 0x46, 0x00, 0x00, 0x00, /* FF10 */ - 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, /* FF18 */ - /* Japanese special keys */ - 0x00, 0x29, 0x7B, 0x79, 0x70, 0x00, 0x00, 0x70, /* FF20 */ - 0x00, 0x00, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, /* FF28 */ - /* Korean special keys (FF31-) */ - 0x3A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* FF30 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* FF38 */ - /* unused */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* FF40 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* FF48 */ - /* cursor keys */ - 0x147, 0x14B, 0x148, 0x14D, 0x150, 0x149, 0x151, 0x14F, /* FF50 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* FF58 */ - /* misc keys */ - /*?*/ 0, 0x137, /*?*/ 0, 0x152, 0x00, 0x00, 0x00, 0x15D, /* FF60 */ - /*?*/ 0, /*?*/ 0, 0x38, 0x146, 0x00, 0x00, 0x00, 0x00, /* FF68 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* FF70 */ - /* keypad keys */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x145, /* FF78 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* FF80 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x11C, 0x00, 0x00, /* FF88 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x4B, 0x48, /* FF90 */ - 0x4D, 0x50, 0x49, 0x51, 0x4F, 0x4C, 0x52, 0x53, /* FF98 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* FFA0 */ - 0x00, 0x00, 0x37, 0x4E, 0x53, 0x4A, 0x53, 0x135, /* FFA8 */ - 0x52, 0x4F, 0x50, 0x51, 0x4B, 0x4C, 0x4D, 0x47, /* FFB0 */ - 0x48, 0x49, 0x00, 0x00, 0x00, 0x00, /* FFB8 */ - /* function keys */ - 0x3B, 0x3C, - 0x3D, 0x3E, 0x3F, 0x40, 0x41, 0x42, 0x43, 0x44, /* FFC0 */ - 0x57, 0x58, 0x5B, 0x5C, 0x5D, 0x00, 0x00, 0x00, /* FFC8 */ - 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 */ - 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 */ -}; - static const WORD xfree86_vendor_key_vkey[256] = { 0, 0, 0, 0, 0, 0, 0, 0, /* 1008FF00 */ @@ -1339,7 +1332,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); } if (event->xkeymap.key_vector[i] & (1<<j)) keys[vkey & 0xff].pressed = TRUE; @@ -1525,10 +1518,13 @@ BOOL X11DRV_KeyEvent( HWND hwnd, XEvent *xev ) if (buf != Str) free( Str ); + scan = keyc2scan( event->keycode ); + /* Handle BREAK */ + if (scan == 0x21d && event->state & ControlMask) + scan = 0x146; 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); @@ -1537,6 +1533,10 @@ BOOL X11DRV_KeyEvent( HWND hwnd, XEvent *xev ) if (!vkey) return FALSE; + /* PAUSE, NUMLOCK are special */ + if (scan == 0x21d) scan = 0x45; + else if (scan == 0x45) scan = 0x145; + dwFlags = 0; if ( event->type == KeyRelease ) dwFlags |= KEYEVENTF_KEYUP; if ( scan & 0x100 ) dwFlags |= KEYEVENTF_EXTENDEDKEY; @@ -1843,7 +1843,7 @@ 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]; @@ -1888,23 +1888,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; @@ -1936,16 +1931,13 @@ 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]; } } } 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 +2046,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, @@ -2388,7 +2366,7 @@ UINT X11DRV_MapVirtualKeyEx( UINT wCode, UINT wMapType, HKL hkl ) { if ((keyc2vkey[keyc] & 0xFF) == wCode) { - ret = keyc2scan[keyc] & 0xFF; + ret = keyc2scan( keyc ) & 0xFF; break; } } @@ -2404,7 +2382,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 ) & 0xFF) == (wCode & 0xFF)) { ret = keyc2vkey[keyc] & 0xFF; /* Only stop if it's not a numpad vkey; otherwise keep @@ -2575,9 +2553,17 @@ 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; + /* PAUSE, NUMLOCK are special */ + if (scanCode == 0x45) + keyi = KEY_PAUSE + 8; + else if (scanCode == 0x145) + keyi = KEY_NUMLOCK + 8; + else + { + for (keyi=min_keycode; keyi<=max_keycode; keyi++) + if (keyc2scan( keyi ) == scanCode) + break; + } if (keyi <= max_keycode) { INT rc; -- 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 | 40 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/dlls/user32/tests/input.c b/dlls/user32/tests/input.c index 9c9b83e1578..93d8061a166 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"}, + {0x01450000, "Num Lock", .todo_value = TRUE}, + {0x02450000, "Pause"}, + {0xe0450000, "Pause"}, + {0xe1450000, "Num Lock", .todo_value = TRUE}, + {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,16 @@ static void test_keynames(void) len = GetKeyNameTextA(i << 16, buff, sizeof(buff)); ok(len || !buff[0], "%d: Buffer is not zeroed\n", i); } + for (i = 0; i < ARRAY_SIZE(tests); i++) + { + winetest_push_context("scancode %.4x", (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"); + if (us_kbd) 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 93d8061a166..09d5fd90559 100644 --- a/dlls/user32/tests/input.c +++ b/dlls/user32/tests/input.c @@ -3259,10 +3259,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 }, @@ -3319,6 +3374,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
On Thu Jun 4 09:18:54 2026 +0000, Matteo Bruni wrote:
Yep. In practice I think I'll extract the required part of the header and put it at the top of keyboard.c, or somewhere in include/wine/. Actually, at that point I could even make that unconditional, getting rid of the `#include` entirely. I'm going to try that and see how it looks. I basically did that, except I kept (retyped, actually) only the defines that we're using. Let me know how it looks to you.
-- https://gitlab.winehq.org/wine/wine/-/merge_requests/10963#note_142205
On Thu Jun 4 16:04:20 2026 +0000, Matteo Bruni wrote:
I basically did that, except I kept (retyped, actually) only the defines that we're using. Let me know how it looks to you. Sorry I actually went to see where this input-event-codes.h include came from and I can't find any previous of the patches introducing it? Is it something you added? I'm not completely sure that introducing a Wine-private header with the definitions duplicated is a good idea when the header is supposed to come from the platform.
I'm not too afraid of the values changing but it still doesn't seem right. If some platform doesn't have it, which should not be a very common situation, I think we could perhaps hardcode for instance `KEY_KPDOT`, but leave the rest behind a #ifdef with a FIXME? -- https://gitlab.winehq.org/wine/wine/-/merge_requests/10963#note_142280
Seem weird and unfortunate to have these special cases isolated out of the mapping helpers and duplicated around. Same for BREAK above. Why can't they be handled in the keyc2scan mapping? -- https://gitlab.winehq.org/wine/wine/-/merge_requests/10963#note_142281
On Fri Jun 5 06:45:31 2026 +0000, Rémi Bernon wrote:
Sorry I actually went to see where this input-event-codes.h include came from and I can't find any previous of the patches introducing it? Is it something you added? I'm not completely sure that introducing a Wine-private header with the definitions duplicated is a good idea when the header is supposed to come from the platform. I'm not too afraid of the values changing but it still doesn't seem right. If some platform doesn't have it, which should not be a very common situation, I think we could perhaps hardcode for instance `KEY_KPDOT`, but leave the rest behind a #ifdef with a FIXME? Gitlab probably doesn't make this as easy as it should be... From an older commit message, when I had the conditional `#include`:
This adds a dependency on linux/input.h or dev/evdev/input.h, on Linux
and FreeBSD respectively. It also includes FreeBSD's
input-event-codes.h as of the time of writing, for use where no system
header is found.
The latter was to make Gitlab's macOS CI happy. I don't know that we care a whole lot for winex11 to work on macOS (actually, why is it being built in the first place? Just to have an extra check?) The FreeBSD header seems to be an older version of the Linux one, with different attribution and license. The `#define`s we use match between the two, the Linux one has a few additional ones. I can't imagine the existing values changing in the future.
If some platform doesn't have it, which should not be a very common situation, I think we could perhaps hardcode for instance `KEY_KPDOT`, but leave the rest behind a #ifdef with a FIXME?
Like skipping the whole switch if some (arbitrarily picked) `#define` is missing? I can do that, but I'm not sure I like it a ton. What about reintroducing the system `#include`s, leaving my tiny header only for the fallback case, AND printing a `FIXME("External header not found, using hardcoded keycode values\n")` if we happen to use it? -- https://gitlab.winehq.org/wine/wine/-/merge_requests/10963#note_142286
Like skipping the whole switch if some (arbitrarily picked) `#define` is missing? I can do that, but I'm not sure I like it a ton.
Yes, I don't think we need anything more if that's only to make the macOS build happy.
What about reintroducing the system `#include`s, leaving my tiny header only for the fallback case, AND printing a `FIXME("External header not found, using hardcoded keycode values\n")` if we happen to use it?
At the very least yes I would prefer that, but I somehow expect @julliard may also prefer not adding an extra header just to support some possibly deprecated corner case. I would approve nevertheless and leave it up to him. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/10963#note_142287
On Fri Jun 5 06:45:06 2026 +0000, Rémi Bernon wrote:
Seem weird and unfortunate to have these special cases isolated out of the mapping helpers and duplicated around. Same for BREAK above. Why can't they be handled in the keyc2scan mapping? Because, as per the test results, you have e.g.:
MapVirtualKeyExA(0x45, MAPVK_VSC_TO_VK_EX, ...); -> VK_NUMLOCK
MapVirtualKeyExA(VK_NUMLOCK, MAPVK_VK_TO_VSC_EX, ...); -> 0x45
MapVirtualKeyExA(0xe11d, MAPVK_VSC_TO_VK_EX, ...); -> VK_PAUSE
MapVirtualKeyExA(VK_PAUSE, MAPVK_VK_TO_VSC_EX, ...); -> 0xe11d
GetKeyNameTextA(0x00450000, ...); -> "Pause"
GetKeyNameTextA(0x01450000, ...); -> "Num Lock"
GetKeyNameTextA(0xe11d0000, ...); -> "Right Ctrl"
i.e. the whole mapping is inconsistent for those particular keys and we don't want to break `MapVirtualKeyExA()`. FWIW, actually pressing the buttons on the keyboard matches with `GetKeyNameTextA()`; the kbdus.dll KBDTABLES on Windows does confirm the `MapVirtualKeyExA()` mapping as per above i.e. the special casing isn't in the `MapVirtualKeyExA()` machinery. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/10963#note_142288
On Fri Jun 5 09:17:51 2026 +0000, Matteo Bruni wrote:
Because, as per the test results, you have e.g.: ``` MapVirtualKeyExA(0x45, MAPVK_VSC_TO_VK_EX, ...); -> VK_NUMLOCK MapVirtualKeyExA(VK_NUMLOCK, MAPVK_VK_TO_VSC_EX, ...); -> 0x45 MapVirtualKeyExA(0xe11d, MAPVK_VSC_TO_VK_EX, ...); -> VK_PAUSE MapVirtualKeyExA(VK_PAUSE, MAPVK_VK_TO_VSC_EX, ...); -> 0xe11d GetKeyNameTextA(0x00450000, ...); -> "Pause" GetKeyNameTextA(0x01450000, ...); -> "Num Lock" GetKeyNameTextA(0xe11d0000, ...); -> "Right Ctrl" GetKeyNameTextA(0x011d0000, ...); -> "Right Ctrl" GetKeyNameTextA(0x001d0000, ...); -> "Ctrl" ``` i.e. the whole mapping is inconsistent for those particular keys and we don't want to break `MapVirtualKeyExA()`. FWIW, actually pressing the buttons on the keyboard matches with `GetKeyNameTextA()`; KBDTABLES from Windows' kbdus.dll confirm the `MapVirtualKeyExA()` mapping as per above i.e. the special casing isn't in the `MapVirtualKeyExA()` machinery. Well maybe there's some extra mapping to be done in `GetKeyNameText` then (although would be IMO better to do that special case in win32u), but the extra cases here should go into keyc2scan. If handling the `BREAK` scancode properly is also important, then it should go in keyc2scan as well, with extra modifier key state parameter, and ideally a similar change done on the winewayland side.
And probably then while we're fixing exotic scancodes, maybe worth adding the extra cases for PrtSc / SysRq while at it. According to https://kbdlayout.info/kbdus/scancodes, I expect it to be something like: `scan == 0x12a && mod & (Ctrl | Shift) => 0x137` and `scan == 0x12a && mod & (Alt) => 0x54`. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/10963#note_142289
On Fri Jun 5 09:29:46 2026 +0000, Rémi Bernon wrote:
Well maybe there's some extra mapping to be done in `GetKeyNameText` then (although would be IMO better to do that special case in win32u), but the extra cases here should go into keyc2scan. If handling the `BREAK` scancode properly is also important, then it should go in keyc2scan as well, with extra modifier key state parameter, and ideally a similar change done on the winewayland side. And probably then while we're fixing exotic scancodes, maybe worth adding the extra cases for PrtSc / SysRq while at it. According to https://kbdlayout.info/kbdus/scancodes, I expect it to be something like: `scan == 0x12a && mod & (Ctrl | Shift) => 0x137` and `scan == 0x12a && mod & (Alt) => 0x54`. To be more precise, what I find unfortunate is to have special cases in driver-specific code. Are these key names causing issues in some way? If so they should probably be special cased in win32u, overriding any driver-specific code. If not, maybe we shouldn't bother about them.
-- https://gitlab.winehq.org/wine/wine/-/merge_requests/10963#note_142290
On Fri Jun 5 09:35:46 2026 +0000, Rémi Bernon wrote:
To be more precise, what I find unfortunate is to have special cases in driver-specific code. Are these key names causing issues in some way? If so they should probably be special cased in win32u, overriding any driver-specific code. If not, maybe we shouldn't bother about them. Ah, all very good points. Yeah, I guess winewayland needs the same. I'll have a look.
For the records, this whole VK_PAUSE digression started because of the dinput:device8 `test_scan_codes()` test failure, so there are some visible effects that applications probably care about. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/10963#note_142291
participants (4)
-
Matteo Bruni -
Matteo Bruni (@Mystral) -
Rémi Bernon -
Rémi Bernon (@rbernon)