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