Use XKB, if available, to map key codes to scan codes using key names instead of using hard coded keyboard layouts. Otherwise, for unsupported keyboard layouts key codes are mapped to incorrect scan codes.
Signed-off-by: Timo Zuccarello timo@zuccarello.eu --- dlls/winex11.drv/keyboard.c | 312 +++++++++++++++++++++++++++++++++++- 1 file changed, 308 insertions(+), 4 deletions(-)
diff --git a/dlls/winex11.drv/keyboard.c b/dlls/winex11.drv/keyboard.c index c6eab6f5cfa..7f29314d582 100644 --- a/dlls/winex11.drv/keyboard.c +++ b/dlls/winex11.drv/keyboard.c @@ -1416,6 +1416,284 @@ BOOL X11DRV_KeyEvent( HWND hwnd, XEvent *xev ) return TRUE; }
+/********************************************************************** + * 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_KEYBOARD_DetectLayout * @@ -1596,6 +1874,9 @@ void X11DRV_InitKeyboard( Display *display ) { 0, 0 } }; int vkey_range; +#ifdef HAVE_XKB + XkbDescPtr kb_desc; +#endif
EnterCriticalSection( &kbd_section ); XDisplayKeycodes(display, &min_keycode, &max_keycode); @@ -1643,6 +1924,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]; @@ -1654,20 +1943,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; @@ -1703,7 +2004,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]; } } @@ -1716,6 +2017,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++) {