Resubmitted as MR, the original patch was https://source.winehq.org/patches/data/232032 (not assigned yet).
From: Timo Zuccarello timo@zuccarello.eu
Use XKB, if available, to map key codes to scan codes using key names instead of using hard coded keyboard layouts.
Signed-off-by: Timo Zuccarello timo@zuccarello.eu --- dlls/winex11.drv/keyboard.c | 311 +++++++++++++++++++++++++++++++++++- 1 file changed, 307 insertions(+), 4 deletions(-)
diff --git a/dlls/winex11.drv/keyboard.c b/dlls/winex11.drv/keyboard.c index 7b4ad9acd8d..f70c5c16f2a 100644 --- a/dlls/winex11.drv/keyboard.c +++ b/dlls/winex11.drv/keyboard.c @@ -1128,6 +1128,283 @@ static WORD EVENT_event_to_vkey( XIC xic, XKeyEvent *e) return keyc2vkey[e->keycode]; }
+/********************************************************************** + * scancode_from_key_name + * + * Maps XKB names to scancodes. + */ +static WORD +scancode_from_key_name( char* keyn ) +{ + /* Comments assume US QWERTY layout. */ + /* These are obtained from the Keyboard Scan Code Specification. */ +#ifdef HAVE_XKB + if (strcmp(keyn, "TLDE") == 0) { /* Tilde */ + return 0x29; + } else if (strcmp(keyn, "AE01") == 0) { /* 1 */ + return 0x02; + } else if (strcmp(keyn, "AE02") == 0) { + return 0x03; + } else if (strcmp(keyn, "AE03") == 0) { + return 0x04; + } else if (strcmp(keyn, "AE04") == 0) { + return 0x05; + } else if (strcmp(keyn, "AE05") == 0) { + return 0x06; + } else if (strcmp(keyn, "AE06") == 0) { + return 0x07; + } else if (strcmp(keyn, "AE07") == 0) { + return 0x08; + } else if (strcmp(keyn, "AE08") == 0) { + return 0x09; + } else if (strcmp(keyn, "AE09") == 0) { /* 9 */ + return 0x0a; + } else if (strcmp(keyn, "AE10") == 0) { /* 0 */ + return 0x0b; + } else if (strcmp(keyn, "AE11") == 0) { /* Minus */ + return 0x0c; + } else if (strcmp(keyn, "AE12") == 0) { /* Equals */ + return 0x0d; + } else if (strcmp(keyn, "BKSP") == 0) { /* Backspace */ + return 0x0e; + } else if (strcmp(keyn, "TAB") == 0) { /* Tab */ + return 0x0f; + } else if (strcmp(keyn, "AD01") == 0) { /* Q */ + return 0x10; + } else if (strcmp(keyn, "AD02") == 0) { + return 0x11; + } else if (strcmp(keyn, "AD03") == 0) { + return 0x12; + } else if (strcmp(keyn, "AD04") == 0) { + return 0x13; + } else if (strcmp(keyn, "AD05") == 0) { + return 0x14; + } else if (strcmp(keyn, "AD06") == 0) { + return 0x15; + } else if (strcmp(keyn, "AD07") == 0) { + return 0x16; + } else if (strcmp(keyn, "AD08") == 0) { + return 0x17; + } else if (strcmp(keyn, "AD09") == 0) { + return 0x18; + } else if (strcmp(keyn, "AD10") == 0) { + return 0x19; + } else if (strcmp(keyn, "AD11") == 0) { /* Left Bracket */ + return 0x1a; + } else if (strcmp(keyn, "AD12") == 0) { /* Right Bracket */ + return 0x1b; + } else if (strcmp(keyn, "BKSL") == 0) { /* Backslash (See key 29 in Scancode Specification.) */ + return 0x2b; + } else if (strcmp(keyn, "CAPS") == 0) { /* Caps Lock */ + return 0x3a; + } else if (strcmp(keyn, "AC01") == 0) { /* A */ + return 0x1e; + } else if (strcmp(keyn, "AC02") == 0) { + return 0x1f; + } else if (strcmp(keyn, "AC03") == 0) { + return 0x20; + } else if (strcmp(keyn, "AC04") == 0) { + return 0x21; + } else if (strcmp(keyn, "AC05") == 0) { + return 0x22; + } else if (strcmp(keyn, "AC06") == 0) { + return 0x23; + } else if (strcmp(keyn, "AC07") == 0) { + return 0x24; + } else if (strcmp(keyn, "AC08") == 0) { + return 0x25; + } else if (strcmp(keyn, "AC09") == 0) { /* L */ + return 0x26; + } else if (strcmp(keyn, "AC10") == 0) { /* Semicolon */ + return 0x27; + } else if (strcmp(keyn, "AC11") == 0) { /* Apostrophe */ + return 0x28; + } else if (strcmp(keyn, "AC12") == 0) { /* Key on international keyboards left of enter. + (See key 42 in Scancode specification.) */ + return 0x2B; + } else if (strcmp(keyn, "RTRN") == 0) { /* Enter */ + return 0x1c; + } else if (strcmp(keyn, "LFSH") == 0) { /* Left Shift */ + return 0x2a; + } else if (strcmp(keyn, "LSGT") == 0) { /* <> key (See key 45 in Scancode specification.) */ + return 0x56; + } else if (strcmp(keyn, "AB01") == 0) { /* Z */ + return 0x2c; + } else if (strcmp(keyn, "AB02") == 0) { /* X */ + return 0x2d; + } else if (strcmp(keyn, "AB03") == 0) { + return 0x2e; + } else if (strcmp(keyn, "AB04") == 0) { + return 0x2f; + } else if (strcmp(keyn, "AB05") == 0) { + return 0x30; + } else if (strcmp(keyn, "AB06") == 0) { + return 0x31; + } else if (strcmp(keyn, "AB07") == 0) { + return 0x32; + } else if (strcmp(keyn, "AB08") == 0) { /* Comma */ + return 0x33; + } else if (strcmp(keyn, "AB09") == 0) { /* Period */ + return 0x34; + } else if (strcmp(keyn, "AB10") == 0) { /* Slash */ + return 0x35; + } else if (strcmp(keyn, "AB11") == 0) { /* Brazilian key (See key 56 in Scancode specification.) */ + return 0x73; + } else if (strcmp(keyn, "RTSH") == 0) { /* Right Shift */ + return 0x36; + } else if (strcmp(keyn, "LCTL") == 0) { /* Left Control */ + return 0x1d; + } else if (strcmp(keyn, "LALT") == 0) { /* Left Alt */ + return 0x38; + } else if (strcmp(keyn, "SPCE") == 0) { /* Space */ + return 0x39; + } else if (strcmp(keyn, "RALT") == 0) { /* Right Alt */ + return 0x138; + } else if (strcmp(keyn, "LVL3") == 0) { /* More names of Right Alt */ + return 0x138; + } else if (strcmp(keyn, "MDSW") == 0) { /* More names of Right Alt */ + return 0x138; + } else if (strcmp(keyn, "ALGR") == 0) { /* More names of Right Alt */ + return 0x138; + } else if (strcmp(keyn, "RCTL") == 0) { /* Right Control */ + return 0x11d; + } else if (strcmp(keyn, "INS") == 0) { /* Insert */ + return 0x152; + } else if (strcmp(keyn, "DELE") == 0) { /* Delete */ + return 0x153; + } else if (strcmp(keyn, "LEFT") == 0) { /* Left Arrow */ + return 0x14b; + } else if (strcmp(keyn, "HOME") == 0) { /* Home */ + return 0x147; + } else if (strcmp(keyn, "END") == 0) { /* End */ + return 0x14f; + } else if (strcmp(keyn, "UP") == 0) { /* Up Arrow */ + return 0x148; + } else if (strcmp(keyn, "DOWN") == 0) { /* Down Arrow */ + return 0x150; + } else if (strcmp(keyn, "PGUP") == 0) { /* Page Up */ + return 0x149; + } else if (strcmp(keyn, "PGDN") == 0) { /* Page Down */ + return 0x151; + } else if (strcmp(keyn, "RGHT") == 0) { /* Right Arrow */ + return 0x14d; + } else if (strcmp(keyn, "NMLK") == 0) { /* Num Lock */ + return 0x45; + } else if (strcmp(keyn, "KP7") == 0) { + return 0x47; + } else if (strcmp(keyn, "KP4") == 0) { + return 0x4b; + } else if (strcmp(keyn, "KP1") == 0) { + return 0x4f; + } else if (strcmp(keyn, "KPDV") == 0) { /* Numeric / */ + return 0x00; + } else if (strcmp(keyn, "KP8") == 0) { + return 0x48; + } else if (strcmp(keyn, "KP5") == 0) { + return 0x4c; + } else if (strcmp(keyn, "KP2") == 0) { + return 0x50; + } else if (strcmp(keyn, "KP0") == 0) { + return 0x52; + } else if (strcmp(keyn, "KPMU") == 0) { /* Numeric * */ + return 0x37; + } else if (strcmp(keyn, "KP9") == 0) { /* Numeric 9 */ + return 0x49; + } else if (strcmp(keyn, "KP6") == 0) { /* Numeric 6 */ + return 0x4d; + } else if (strcmp(keyn, "KP3") == 0) { /* Numeric 3 */ + return 0x51; + } else if (strcmp(keyn, "KPDL") == 0) { /* Numeric . */ + return 0x53; + } else if (strcmp(keyn, "KPCO") == 0) { /* Numeric . */ + return 0x53; + } else if (strcmp(keyn, "KPSU") == 0) { /* Numeric - */ + return 0x4a; + } else if (strcmp(keyn, "KPAD") == 0) { /* Numeric + */ + return 0x4e; + } else if (strcmp(keyn, "KPEQ") == 0) { + return 0x00; + } else if (strcmp(keyn, "KPEN") == 0) { /* Numeric Enter */ + return 0x11c; + } else if (strcmp(keyn, "ESC") == 0) { /* Escape */ + return 0x01; + } else if (strcmp(keyn, "FK01") == 0) { /* F1 */ + return 0x3b; + } else if (strcmp(keyn, "FK02") == 0) { + return 0x3c; + } else if (strcmp(keyn, "FK03") == 0) { + return 0x3d; + } else if (strcmp(keyn, "FK04") == 0) { + return 0x3e; + } else if (strcmp(keyn, "FK05") == 0) { + return 0x3f; + } else if (strcmp(keyn, "FK06") == 0) { + return 0x40; + } else if (strcmp(keyn, "FK07") == 0) { + return 0x41; + } else if (strcmp(keyn, "FK08") == 0) { + return 0x42; + } else if (strcmp(keyn, "FK09") == 0) { + return 0x43; + } else if (strcmp(keyn, "FK10") == 0) { + return 0x44; + } else if (strcmp(keyn, "FK11") == 0) { + return 0x57; + } else if (strcmp(keyn, "FK12") == 0) { /* F12 */ + return 0x58; + } else if (strcmp(keyn, "FK13") == 0) { /* F13 */ + return 0x00; + } else if (strcmp(keyn, "FK14") == 0) { + return 0x00; + } else if (strcmp(keyn, "FK15") == 0) { + return 0x00; + } else if (strcmp(keyn, "FK16") == 0) { + return 0x00; + } else if (strcmp(keyn, "FK17") == 0) { + return 0x00; + } else if (strcmp(keyn, "FK18") == 0) { + return 0x00; + } else if (strcmp(keyn, "FK19") == 0) { + return 0x00; + } else if (strcmp(keyn, "FK20") == 0) { + return 0x00; + } else if (strcmp(keyn, "FK21") == 0) { + return 0x00; + } else if (strcmp(keyn, "FK22") == 0) { + return 0x00; + } else if (strcmp(keyn, "FK23") == 0) { + return 0x00; + } else if (strcmp(keyn, "FK24") == 0) { + return 0x00; + } else if (strcmp(keyn, "FK25") == 0) { /* F25 */ + return 0x00; + } else if (strcmp(keyn, "PRSC") == 0) { + return 0x00; + } else if (strcmp(keyn, "SCLK") == 0) { + return 0x46; + } else if (strcmp(keyn, "PAUS") == 0) { + return 0x45; + } else if (strcmp(keyn, "LWIN") == 0) { + return 0x15b; + } else if (strcmp(keyn, "RWIN") == 0) { + return 0x15c; + } else if (strcmp(keyn, "MENU") == 0) { + return 0x15d; + } else if (strcmp(keyn, "POWR") == 0) { + return 0x15e; + } else if (strcmp(keyn, "KATA") == 0) { /* Katakana */ + return 0x70; + } else if (strcmp(keyn, "XFER") == 0) { /* Convert/Henkan */ + return 0x79; + } else if (strcmp(keyn, "NFER") == 0) { /* Non Convert/Muhenkan */ + return 0x7b; + } else { + return 0x00; + } +#else + return 0x00; +#endif +}
/*********************************************************************** * X11DRV_send_keyboard_input @@ -1594,6 +1871,9 @@ void X11DRV_InitKeyboard( Display *display ) { 0, 0 } }; int vkey_range; +#ifdef HAVE_XKB + XkbDescPtr kb_desc; +#endif
pthread_mutex_lock( &kbd_mutex ); XDisplayKeycodes(display, &min_keycode, &max_keycode); @@ -1641,6 +1921,14 @@ void X11DRV_InitKeyboard( Display *display ) e2.type = KeyPress;
memset(keyc2vkey, 0, sizeof(keyc2vkey)); +#ifdef HAVE_XKB + kb_desc = XkbGetMap(display, 0, XkbUseCoreKbd); + if (use_xkb) + { + /* Allocate and fill the keycode-name map. */ + XkbGetNames(display, XkbKeyNamesMask, kb_desc); + } +#endif for (keyc = min_keycode; keyc <= max_keycode; keyc++) { char buf[30]; @@ -1652,20 +1940,32 @@ void X11DRV_InitKeyboard( Display *display ) vkey = 0; scan = 0; if (keysym) /* otherwise, keycode not used */ { +#ifdef HAVE_XKB + if (use_xkb) + { + /* Returned key name may not be null-terminated, if it is exactly XkbKeyNameLength long, add a null to be sure. */ + char keyn[XkbKeyNameLength+1]; + memset(keyn, 0, XkbKeyNameLength+1); + memcpy(keyn, kb_desc->names->keys[keyc].name, XkbKeyNameLength); + + /* Translate key name to a scan code. */ + scan = scancode_from_key_name(keyn); + } +#endif if ((keysym >> 8) == 0xFF) /* non-character key */ { vkey = nonchar_key_vkey[keysym & 0xff]; - scan = nonchar_key_scan[keysym & 0xff]; + scan = scan ? 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; + scan = scan ? scan : 0x100; vkey |= 0x100; } else if (keysym == 0x20) { /* Spacebar */ vkey = VK_SPACE; - scan = 0x39; + scan = scan ? scan : 0x39; } else if (have_chars) { /* we seem to need to search the layout-dependent scancodes */ int maxlen=0,maxval=-1,ok; @@ -1701,7 +2001,7 @@ void X11DRV_InitKeyboard( Display *display ) /* 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]; + scan = scan ? scan : (*lscan)[maxval]; vkey = (*lvkey)[maxval]; } } @@ -1714,6 +2014,9 @@ void X11DRV_InitKeyboard( Display *display ) vkey_used[(vkey & 0xff)] = 1; } /* for */
+#ifdef HAVE_XKB + XkbFreeClientMap(kb_desc, 0, TRUE); +#endif #define VKEY_IF_NOT_USED(vkey) (vkey_used[(vkey)] ? 0 : (vkey_used[(vkey)] = 1, (vkey))) for (keyc = min_keycode; keyc <= max_keycode; keyc++) {