[PATCH v4 0/4] MR10550: winex11: Keyboard layouts rework, part 1
The idea is to improve the detection of the available keyboard layouts, normally via Xkb group names + xkbregistry, and then propagate the current layout to win32u and the PE / win32 side state. Along the way also start using `KBDTABLES` and the related newer win32u machinery that was introduced for winewayland. I'm taking a lot of inspiration (and also actual patches, or pieces of code) from the winewayland driver and Rémi's draft MR !2122. The main difference from that attempt is that I'm going to keep around `X11DRV_KEYBOARD_DetectLayout()` and use it as a fallback detection mechanism (e.g. for vncserver), but still move winex11 to `KBDTABLES` and such. You can find the current (still WIP, especially for later patches) branch at https://gitlab.winehq.org/Mystral/wine/-/commits/hl-kbl?ref_type=heads -- v4: winex11: Decode current keyboard layout from the Xkb group name. winex11: Look up keysyms for the currently selected Xkb group in X11DRV_KEYBOARD_DetectLayout(). winex11: Factor out mapping keycodes to scancodes / keysyms into a separate function. winex11: Get Xkb group names. https://gitlab.winehq.org/wine/wine/-/merge_requests/10550
From: Matteo Bruni <mbruni@codeweavers.com> --- dlls/winex11.drv/keyboard.c | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/dlls/winex11.drv/keyboard.c b/dlls/winex11.drv/keyboard.c index e2281bef017..fbccc469a09 100644 --- a/dlls/winex11.drv/keyboard.c +++ b/dlls/winex11.drv/keyboard.c @@ -1561,6 +1561,11 @@ void X11DRV_InitKeyboard( Display *display ) char ckey[4]={0,0,0,0}; const char (*lkey)[MAIN_LEN][4]; char vkey_used[256] = { 0 }; + XkbStateRec xkb_state; + char *xkb_group_names[4]; + unsigned int xkb_group, xkb_group_count; + XkbDescRec *xkb_desc; + Status s; /* Ranges of OEM, function key, and character virtual key codes. * Don't include those handled specially in X11DRV_ToUnicodeEx and @@ -1611,6 +1616,29 @@ void X11DRV_InitKeyboard( Display *display ) } XFreeModifiermap(mmp); + s = XkbGetState( display, XkbUseCoreKbd, &xkb_state ); + xkb_group = s ? 0 : xkb_state.group; + TRACE("s %#x, current Xkb group %u\n", s, xkb_group); + xkb_desc = XkbGetMap( display, XkbAllClientInfoMask, XkbUseCoreKbd ); + if (xkb_desc) + { + XkbGetNames( display, XkbGroupNamesMask, xkb_desc ); + for (i = 0; i < ARRAY_SIZE(xkb_desc->names->groups); i++) + { + if (!xkb_desc->names->groups[i]) + break; + } + xkb_group_count = i; + if (!XGetAtomNames( display, xkb_desc->names->groups, xkb_group_count, xkb_group_names )) + xkb_group_count = 0; + for (i = 0; i < xkb_group_count; i++) + { + TRACE("group %u name %s\n", i, debugstr_a(xkb_group_names[i])); + XFree( xkb_group_names[i] ); + } + XkbFreeKeyboard( xkb_desc, XkbNamesMask, True ); + } + /* Detect the keyboard layout */ X11DRV_KEYBOARD_DetectLayout( display ); lkey = main_key_tab[kbd_layout].key; -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10550
From: Matteo Bruni <mbruni@codeweavers.com> We'll entirely get rid of the new x11drv_init_layout() function after introducing the new implementation based on KBDTABLES. --- dlls/winex11.drv/keyboard.c | 282 +++++++++++++++++++----------------- 1 file changed, 152 insertions(+), 130 deletions(-) diff --git a/dlls/winex11.drv/keyboard.c b/dlls/winex11.drv/keyboard.c index fbccc469a09..a9f3ac04725 100644 --- a/dlls/winex11.drv/keyboard.c +++ b/dlls/winex11.drv/keyboard.c @@ -1438,7 +1438,6 @@ BOOL X11DRV_KeyEvent( HWND hwnd, XEvent *xev ) /********************************************************************** * X11DRV_KEYBOARD_DetectLayout * - * Called from X11DRV_InitKeyboard * This routine walks through the defined keyboard layouts and selects * whichever matches most closely. * kbd_section must be held. @@ -1546,33 +1545,16 @@ X11DRV_KEYBOARD_DetectLayout( Display *display ) TRACE("detected layout is \"%s\"\n", main_key_tab[kbd_layout].comment); } - -/********************************************************************** - * X11DRV_InitKeyboard - */ -void X11DRV_InitKeyboard( Display *display ) +static void x11drv_init_layout( Display *display ) { - XModifierKeymap *mmp; - KeySym keysym; - KeyCode *kcp; - XKeyEvent e2; - WORD scan, vkey; - int keyc, i, keyn, syms; - char ckey[4]={0,0,0,0}; - const char (*lkey)[MAIN_LEN][4]; - char vkey_used[256] = { 0 }; - XkbStateRec xkb_state; - char *xkb_group_names[4]; - unsigned int xkb_group, xkb_group_count; - XkbDescRec *xkb_desc; - Status s; - /* Ranges of OEM, function key, and character virtual key codes. * Don't include those handled specially in X11DRV_ToUnicodeEx and * X11DRV_MapVirtualKeyEx, like VK_NUMPAD0 - VK_DIVIDE. */ - static const struct { + static const struct + { WORD first, last; - } vkey_ranges[] = { + } vkey_ranges[] = + { { VK_OEM_1, VK_OEM_3 }, { VK_OEM_4, VK_OEM_8 }, { VK_OEM_AX, VK_ICO_00 }, @@ -1585,62 +1567,14 @@ void X11DRV_InitKeyboard( Display *display ) { 0, 0 } }; int vkey_range; + KeySym keysym; + XKeyEvent e2; + WORD scan, vkey; + int keyc, i, keyn, syms; + char ckey[4] = { 0, 0, 0, 0 }; + const char (*lkey)[MAIN_LEN][4]; + char vkey_used[256] = { 0 }; - pthread_mutex_lock( &kbd_mutex ); - XDisplayKeycodes(display, &min_keycode, &max_keycode); - XFree( XGetKeyboardMapping( display, min_keycode, max_keycode + 1 - min_keycode, &keysyms_per_keycode ) ); - - mmp = XGetModifierMapping(display); - kcp = mmp->modifiermap; - for (i = 0; i < 8; i += 1) /* There are 8 modifier keys */ - { - int j; - - for (j = 0; j < mmp->max_keypermod; j += 1, kcp += 1) - if (*kcp) - { - int k; - - for (k = 0; k < keysyms_per_keycode; k += 1) - if (XkbKeycodeToKeysym( display, *kcp, 0, k ) == XK_Num_Lock) - { - NumLockMask = 1 << i; - TRACE_(key)("NumLockMask is %x\n", NumLockMask); - } - else if (XkbKeycodeToKeysym( display, *kcp, 0, k ) == XK_Scroll_Lock) - { - ScrollLockMask = 1 << i; - TRACE_(key)("ScrollLockMask is %x\n", ScrollLockMask); - } - } - } - XFreeModifiermap(mmp); - - s = XkbGetState( display, XkbUseCoreKbd, &xkb_state ); - xkb_group = s ? 0 : xkb_state.group; - TRACE("s %#x, current Xkb group %u\n", s, xkb_group); - xkb_desc = XkbGetMap( display, XkbAllClientInfoMask, XkbUseCoreKbd ); - if (xkb_desc) - { - XkbGetNames( display, XkbGroupNamesMask, xkb_desc ); - for (i = 0; i < ARRAY_SIZE(xkb_desc->names->groups); i++) - { - if (!xkb_desc->names->groups[i]) - break; - } - xkb_group_count = i; - if (!XGetAtomNames( display, xkb_desc->names->groups, xkb_group_count, xkb_group_names )) - xkb_group_count = 0; - for (i = 0; i < xkb_group_count; i++) - { - TRACE("group %u name %s\n", i, debugstr_a(xkb_group_names[i])); - XFree( xkb_group_names[i] ); - } - XkbFreeKeyboard( xkb_desc, XkbNamesMask, True ); - } - - /* Detect the keyboard layout */ - X11DRV_KEYBOARD_DetectLayout( display ); lkey = main_key_tab[kbd_layout].key; syms = (keysyms_per_keycode > 4) ? 4 : keysyms_per_keycode; @@ -1668,53 +1602,61 @@ void X11DRV_InitKeyboard( Display *display ) { vkey = nonchar_key_vkey[keysym & 0xff]; scan = nonchar_key_scan[keysym & 0xff]; - /* set extended bit when necessary */ - if (scan & 0x100) vkey |= 0x100; + /* 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; + 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; - for (i=0; i<syms; i++) { - keysym = XkbKeycodeToKeysym( display, keyc, 0, i ); - if ((keysym<0x8000) && (keysym!=' ')) + 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; + + for (i = 0; i < syms; i++) { - if (!XkbTranslateKeySym(display, &keysym, 0, &ckey[i], 1, NULL)) + keysym = XkbKeycodeToKeysym( display, keyc, 0, i ); + if ((keysym < 0x8000) && (keysym != ' ')) { - /* 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; + 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; - const WORD (*lvkey)[MAIN_LEN] = main_key_tab[kbd_layout].vkey; - scan = (*lscan)[maxval]; - vkey = (*lvkey)[maxval]; - } - } + 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; + 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; @@ -1734,7 +1676,7 @@ void X11DRV_InitKeyboard( Display *display ) e2.keycode = (KeyCode)keyc; keysym = XLookupKeysym(&e2, 0); if (!keysym) - continue; + continue; /* find a suitable layout-dependent VK code */ /* (most Winelib apps ought to be able to work without layout tables!) */ @@ -1742,7 +1684,8 @@ void X11DRV_InitKeyboard( Display *display ) { keysym = XLookupKeysym(&e2, i); if ((keysym >= XK_0 && keysym <= XK_9) - || (keysym >= XK_A && keysym <= XK_Z)) { + || (keysym >= XK_A && keysym <= XK_Z)) + { vkey = VKEY_IF_NOT_USED(keysym); } } @@ -1785,7 +1728,7 @@ void X11DRV_InitKeyboard( Display *display ) e2.keycode = (KeyCode)keyc; keysym = XLookupKeysym(&e2, 0); if (!keysym) - continue; + continue; while (vkey && vkey_used[vkey]) { @@ -1807,7 +1750,7 @@ void X11DRV_InitKeyboard( Display *display ) if (TRACE_ON(keyboard)) { TRACE("spare virtual key %04X assigned to keycode %u:\n", - vkey, e2.keycode); + vkey, e2.keycode); TRACE("("); for (i = 0; i < keysyms_per_keycode; i += 1) { @@ -1830,17 +1773,96 @@ void X11DRV_InitKeyboard( Display *display ) /* 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"; + { + 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 */ + /* 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++; - } + TRACE_(key)("assigning scancode %02x to unidentified keycode %u (%s)\n", scan, keyc, ksname); + keyc2scan[keyc] = scan++; + } + } +} + +/********************************************************************** + * X11DRV_InitKeyboard + */ +void X11DRV_InitKeyboard( Display *display ) +{ + XModifierKeymap *mmp; + KeyCode *kcp; + XkbStateRec xkb_state; + char *xkb_group_names[4]; + unsigned int i, xkb_group, xkb_group_count; + XkbDescRec *xkb_desc; + Status s; + + pthread_mutex_lock( &kbd_mutex ); + XDisplayKeycodes(display, &min_keycode, &max_keycode); + XFree( XGetKeyboardMapping( display, min_keycode, max_keycode + 1 - min_keycode, &keysyms_per_keycode ) ); + + mmp = XGetModifierMapping(display); + kcp = mmp->modifiermap; + for (i = 0; i < 8; i += 1) /* There are 8 modifier keys */ + { + int j; + + for (j = 0; j < mmp->max_keypermod; j += 1, kcp += 1) + { + if (*kcp) + { + int k; + + for (k = 0; k < keysyms_per_keycode; k += 1) + { + if (XkbKeycodeToKeysym( display, *kcp, 0, k ) == XK_Num_Lock) + { + NumLockMask = 1 << i; + TRACE_(key)("NumLockMask is %x\n", NumLockMask); + } + else if (XkbKeycodeToKeysym( display, *kcp, 0, k ) == XK_Scroll_Lock) + { + ScrollLockMask = 1 << i; + TRACE_(key)("ScrollLockMask is %x\n", ScrollLockMask); + } + } + } + } + } + XFreeModifiermap(mmp); + + s = XkbGetState( display, XkbUseCoreKbd, &xkb_state ); + xkb_group = s ? 0 : xkb_state.group; + TRACE("s %#x, current Xkb group %u\n", s, xkb_group); + xkb_desc = XkbGetMap( display, XkbAllClientInfoMask, XkbUseCoreKbd ); + if (xkb_desc) + { + XkbGetNames( display, XkbGroupNamesMask, xkb_desc ); + for (i = 0; i < ARRAY_SIZE(xkb_desc->names->groups); i++) + { + if (!xkb_desc->names->groups[i]) + break; + } + xkb_group_count = i; + if (!XGetAtomNames( display, xkb_desc->names->groups, xkb_group_count, xkb_group_names )) + xkb_group_count = 0; + for (i = 0; i < xkb_group_count; i++) + { + TRACE("group %u name %s\n", i, debugstr_a(xkb_group_names[i])); + XFree( xkb_group_names[i] ); + } + XkbFreeKeyboard( xkb_desc, 0, True ); + } + + /* Detect the keyboard layout */ + X11DRV_KEYBOARD_DetectLayout( display ); + + x11drv_init_layout( display ); pthread_mutex_unlock( &kbd_mutex ); } -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10550
From: Matteo Bruni <mbruni@codeweavers.com> --- dlls/winex11.drv/keyboard.c | 44 +++++++++++++++++++++++-------------- 1 file changed, 27 insertions(+), 17 deletions(-) diff --git a/dlls/winex11.drv/keyboard.c b/dlls/winex11.drv/keyboard.c index a9f3ac04725..898286a5e6e 100644 --- a/dlls/winex11.drv/keyboard.c +++ b/dlls/winex11.drv/keyboard.c @@ -1388,15 +1388,6 @@ BOOL X11DRV_KeyEvent( HWND hwnd, XEvent *xev ) pthread_mutex_lock( &kbd_mutex ); - /* If XKB extensions are used, the state mask for AltGr will use the group - index instead of the modifier mask. The group index is set in bits - 13-14 of the state field in the XKeyEvent structure. So if AltGr is - pressed, look if the group index is different than 0. From XKB - extension documentation, the group index for AltGr should be 2 - (event->state = 0x2000). It's probably better to not assume a - predefined group index and find it dynamically - - Ref: X Keyboard Extension: Library specification (section 14.1.1 and 17.1.1) */ /* Save also all possible modifier states. */ AltGrMask = event->state & (0x6000 | Mod1Mask | Mod2Mask | Mod3Mask | Mod4Mask | Mod5Mask); @@ -1443,15 +1434,28 @@ BOOL X11DRV_KeyEvent( HWND hwnd, XEvent *xev ) * kbd_section must be held. */ static void -X11DRV_KEYBOARD_DetectLayout( Display *display ) +X11DRV_KEYBOARD_DetectLayout( Display *display, XModifierKeymap *modmap, unsigned int xkb_group ) { unsigned current, match, mismatch, seq, i, syms; int score, keyc, key, pkey, ok; - KeySym keysym = 0; + KeySym keysym; const char (*lkey)[MAIN_LEN][4]; unsigned max_seq = 0; int max_score = INT_MIN, ismatch = 0; char ckey[256][4]; + unsigned int state, altgr_mod = 0, dummy, mod; + + TRACE("display %p, mmp %p, xkb_group %u\n", display, modmap, xkb_group); + + 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); syms = keysyms_per_keycode; if (syms > 4) { @@ -1460,10 +1464,17 @@ X11DRV_KEYBOARD_DetectLayout( Display *display ) } memset( ckey, 0, sizeof(ckey) ); - for (keyc = min_keycode; keyc <= max_keycode; keyc++) { + for (keyc = min_keycode; keyc <= max_keycode; keyc++) + { /* get data for keycode from X server */ - for (i = 0; i < syms; i++) { - if (!(keysym = XkbKeycodeToKeysym( display, keyc, 0, i ))) continue; + for (i = 0; i < syms; i++) + { + /* With Xkb, the current group index is encoded in bits 13-14 of the + state field. + + Ref: X Keyboard Extension: Library specification (section 18.1.1) */ + state = xkb_group << 13 | (i & 1 ? ShiftMask : 0) | (i & 2 ? altgr_mod : 0); + if (!XkbLookupKeySym( display, keyc, state, &dummy, &keysym )) continue; /* Allow both one-byte and two-byte national keysyms */ if ((keysym < 0x8000) && (keysym != ' ')) { @@ -1834,7 +1845,6 @@ void X11DRV_InitKeyboard( Display *display ) } } } - XFreeModifiermap(mmp); s = XkbGetState( display, XkbUseCoreKbd, &xkb_state ); xkb_group = s ? 0 : xkb_state.group; @@ -1859,8 +1869,8 @@ void X11DRV_InitKeyboard( Display *display ) XkbFreeKeyboard( xkb_desc, 0, True ); } - /* Detect the keyboard layout */ - X11DRV_KEYBOARD_DetectLayout( display ); + X11DRV_KEYBOARD_DetectLayout( display, mmp, xkb_group ); + XFreeModifiermap(mmp); x11drv_init_layout( display ); -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10550
From: Matteo Bruni <mbruni@codeweavers.com> For large parts lifted from Rémi's winewayland commit d64ea8e4a6c93ba77208cb680b91dc0c2830049d. --- configure | 202 ++++++++++++++++++--------------- configure.ac | 8 +- dlls/winex11.drv/Makefile.in | 4 +- dlls/winex11.drv/keyboard.c | 188 +++++++++++++++++++++++++++++- dlls/winex11.drv/x11drv.h | 1 + dlls/winex11.drv/x11drv_main.c | 2 +- include/config.h.in | 3 + 7 files changed, 306 insertions(+), 102 deletions(-) diff --git a/configure b/configure index d8690c5dd0c..13883909a18 100755 --- a/configure +++ b/configure @@ -700,13 +700,13 @@ PCSCLITE_LIBS PCAP_LIBS WAYLAND_EGL_LIBS WAYLAND_EGL_CFLAGS -XKBREGISTRY_LIBS -XKBREGISTRY_CFLAGS XKBCOMMON_LIBS XKBCOMMON_CFLAGS WAYLAND_SCANNER WAYLAND_CLIENT_LIBS WAYLAND_CLIENT_CFLAGS +XKBREGISTRY_LIBS +XKBREGISTRY_CFLAGS X_LIBS X_CFLAGS CPP @@ -1901,12 +1901,12 @@ EGL_CFLAGS EGL_LIBS XMKMF CPP +XKBREGISTRY_CFLAGS +XKBREGISTRY_LIBS WAYLAND_CLIENT_CFLAGS WAYLAND_CLIENT_LIBS XKBCOMMON_CFLAGS XKBCOMMON_LIBS -XKBREGISTRY_CFLAGS -XKBREGISTRY_LIBS WAYLAND_EGL_CFLAGS WAYLAND_EGL_LIBS INOTIFY_CFLAGS @@ -2761,6 +2761,10 @@ Some influential environment variables: EGL_LIBS Linker flags for egl, overriding pkg-config XMKMF Path to xmkmf, Makefile generator for X Window System CPP C preprocessor + XKBREGISTRY_CFLAGS + C compiler flags for xkbregistry, overriding pkg-config + XKBREGISTRY_LIBS + Linker flags for xkbregistry, overriding pkg-config WAYLAND_CLIENT_CFLAGS C compiler flags for wayland-client, overriding pkg-config WAYLAND_CLIENT_LIBS @@ -2769,10 +2773,6 @@ Some influential environment variables: C compiler flags for xkbcommon, overriding pkg-config XKBCOMMON_LIBS Linker flags for xkbcommon, overriding pkg-config - XKBREGISTRY_CFLAGS - C compiler flags for xkbregistry, overriding pkg-config - XKBREGISTRY_LIBS - Linker flags for xkbregistry, overriding pkg-config WAYLAND_EGL_CFLAGS C compiler flags for wayland-egl, overriding pkg-config WAYLAND_EGL_LIBS @@ -16874,6 +16874,105 @@ else X_LIBS="" fi +if test "x$have_x" != "xno" || "x$with_wayland" != "xno" + then + rm -f conftest.err +if ${XKBREGISTRY_CFLAGS:+false} : +then : + if test ${PKG_CONFIG+y} +then : + XKBREGISTRY_CFLAGS=`$PKG_CONFIG --cflags xkbregistry 2>conftest.err` +fi +fi + +if ${XKBREGISTRY_LIBS:+false} : +then : + if test ${PKG_CONFIG+y} +then : + XKBREGISTRY_LIBS=`$PKG_CONFIG --libs xkbregistry 2>/dev/null` +fi +fi + + +printf "%s\n" "$as_me:${as_lineno-$LINENO}: xkbregistry cflags: $XKBREGISTRY_CFLAGS" >&5 +printf "%s\n" "$as_me:${as_lineno-$LINENO}: xkbregistry libs: $XKBREGISTRY_LIBS" >&5 +if test -s conftest.err; then + printf %s "$as_me:${as_lineno-$LINENO}: xkbregistry errors: " >&5 + cat conftest.err >&5 +fi +rm -f conftest.err +if test -n "$XKBREGISTRY_CFLAGS$XKBREGISTRY_LIBS" +then : + ac_save_CPPFLAGS=$CPPFLAGS +CPPFLAGS="$CPPFLAGS $XKBREGISTRY_CFLAGS" +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for -lxkbregistry" >&5 +printf %s "checking for -lxkbregistry... " >&6; } +if test ${ac_cv_lib_soname_xkbregistry+y} +then : + printf %s "(cached) " >&6 +else case e in #( + e) ac_check_soname_save_LIBS=$LIBS +LIBS="-lxkbregistry $XKBREGISTRY_LIBS $LIBS" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. + The 'extern "C"' is for builds by C++ compilers; + although this is not generally supported in C code supporting it here + has little cost and some practical benefit (sr 110532). */ +#ifdef __cplusplus +extern "C" +#endif +char rxkb_context_new (void); +int +main (void) +{ +return rxkb_context_new (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO" +then : + case "$LIBEXT" in + dll) ac_cv_lib_soname_xkbregistry=`$ac_cv_path_LDD conftest.exe | grep "xkbregistry" | sed -e "s/dll.*/dll/"';2,$d'` ;; + dylib) ac_cv_lib_soname_xkbregistry=`$OTOOL -L conftest$ac_exeext | grep "libxkbregistry\\.[0-9A-Za-z.]*dylib" | sed -e "s/^.*\/\(libxkbregistry\.[0-9A-Za-z.]*dylib\).*$/\1/"';2,$d'` ;; + *) ac_cv_lib_soname_xkbregistry=`$READELF -d conftest$ac_exeext | grep "NEEDED.*libxkbregistry\\.$LIBEXT" | sed -e "s/^.*\\[\\(libxkbregistry\\.$LIBEXT[^ ]*\\)\\].*$/\1/"';2,$d'` + if ${ac_cv_lib_soname_xkbregistry:+false} : +then : + ac_cv_lib_soname_xkbregistry=`$LDD conftest$ac_exeext | grep "libxkbregistry\\.$LIBEXT" | sed -e "s/^.*\(libxkbregistry\.$LIBEXT[^ ]*\).*$/\1/"';2,$d'` +fi ;; + esac +else case e in #( + e) ac_cv_lib_soname_xkbregistry= ;; +esac +fi +rm -f core conftest.err conftest.$ac_objext conftest.beam \ + conftest$ac_exeext conftest.$ac_ext + LIBS=$ac_check_soname_save_LIBS ;; +esac +fi +if ${ac_cv_lib_soname_xkbregistry:+false} : +then : + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: not found" >&5 +printf "%s\n" "not found" >&6; } + XKBREGISTRY_LIBS="" +else case e in #( + e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_soname_xkbregistry" >&5 +printf "%s\n" "$ac_cv_lib_soname_xkbregistry" >&6; } + +printf "%s\n" "#define SONAME_LIBXKBREGISTRY \"$ac_cv_lib_soname_xkbregistry\"" >>confdefs.h + + : ;; +esac +fi +CPPFLAGS=$ac_save_CPPFLAGS +fi + + fi + if test "x$with_wayland" != "xno" then rm -f conftest.err @@ -17090,89 +17189,6 @@ else case e in #( esac fi -CPPFLAGS=$ac_save_CPPFLAGS -fi - - rm -f conftest.err -if ${XKBREGISTRY_CFLAGS:+false} : -then : - if test ${PKG_CONFIG+y} -then : - XKBREGISTRY_CFLAGS=`$PKG_CONFIG --cflags xkbregistry 2>conftest.err` -fi -fi - -if ${XKBREGISTRY_LIBS:+false} : -then : - if test ${PKG_CONFIG+y} -then : - XKBREGISTRY_LIBS=`$PKG_CONFIG --libs xkbregistry 2>/dev/null` -fi -fi - - -printf "%s\n" "$as_me:${as_lineno-$LINENO}: xkbregistry cflags: $XKBREGISTRY_CFLAGS" >&5 -printf "%s\n" "$as_me:${as_lineno-$LINENO}: xkbregistry libs: $XKBREGISTRY_LIBS" >&5 -if test -s conftest.err; then - printf %s "$as_me:${as_lineno-$LINENO}: xkbregistry errors: " >&5 - cat conftest.err >&5 -fi -rm -f conftest.err -if test -n "$XKBREGISTRY_CFLAGS$XKBREGISTRY_LIBS" -then : - ac_save_CPPFLAGS=$CPPFLAGS -CPPFLAGS="$CPPFLAGS $XKBREGISTRY_CFLAGS" -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for rxkb_context_new in -lxkbregistry" >&5 -printf %s "checking for rxkb_context_new in -lxkbregistry... " >&6; } -if test ${ac_cv_lib_xkbregistry_rxkb_context_new+y} -then : - printf %s "(cached) " >&6 -else case e in #( - e) ac_check_lib_save_LIBS=$LIBS -LIBS="-lxkbregistry $XKBREGISTRY_LIBS $LIBS" -cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -/* Override any GCC internal prototype to avoid an error. - Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. - The 'extern "C"' is for builds by C++ compilers; - although this is not generally supported in C code supporting it here - has little cost and some practical benefit (sr 110532). */ -#ifdef __cplusplus -extern "C" -#endif -char rxkb_context_new (void); -int -main (void) -{ -return rxkb_context_new (); - ; - return 0; -} -_ACEOF -if ac_fn_c_try_link "$LINENO" -then : - ac_cv_lib_xkbregistry_rxkb_context_new=yes -else case e in #( - e) ac_cv_lib_xkbregistry_rxkb_context_new=no ;; -esac -fi -rm -f core conftest.err conftest.$ac_objext conftest.beam \ - conftest$ac_exeext conftest.$ac_ext -LIBS=$ac_check_lib_save_LIBS ;; -esac -fi -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_xkbregistry_rxkb_context_new" >&5 -printf "%s\n" "$ac_cv_lib_xkbregistry_rxkb_context_new" >&6; } -if test "x$ac_cv_lib_xkbregistry_rxkb_context_new" = xyes -then : - : -else case e in #( - e) XKBREGISTRY_LIBS="" ;; -esac -fi - CPPFLAGS=$ac_save_CPPFLAGS fi @@ -25174,13 +25190,13 @@ XMKMF = $XMKMF CPP = $CPP X_CFLAGS = $X_CFLAGS X_LIBS = $X_LIBS +XKBREGISTRY_CFLAGS = $XKBREGISTRY_CFLAGS +XKBREGISTRY_LIBS = $XKBREGISTRY_LIBS WAYLAND_CLIENT_CFLAGS = $WAYLAND_CLIENT_CFLAGS WAYLAND_CLIENT_LIBS = $WAYLAND_CLIENT_LIBS WAYLAND_SCANNER = $WAYLAND_SCANNER XKBCOMMON_CFLAGS = $XKBCOMMON_CFLAGS XKBCOMMON_LIBS = $XKBCOMMON_LIBS -XKBREGISTRY_CFLAGS = $XKBREGISTRY_CFLAGS -XKBREGISTRY_LIBS = $XKBREGISTRY_LIBS WAYLAND_EGL_CFLAGS = $WAYLAND_EGL_CFLAGS WAYLAND_EGL_LIBS = $WAYLAND_EGL_LIBS PCAP_LIBS = $PCAP_LIBS diff --git a/configure.ac b/configure.ac index f35ac4376d9..c90fe7d888c 100644 --- a/configure.ac +++ b/configure.ac @@ -1433,6 +1433,12 @@ else X_LIBS="" fi +if test "x$have_x" != "xno" || "x$with_wayland" != "xno" + then + WINE_PACKAGE_FLAGS(XKBREGISTRY,[xkbregistry],,,, + [WINE_CHECK_SONAME(xkbregistry,rxkb_context_new,[:],[XKBREGISTRY_LIBS=""],[$XKBREGISTRY_LIBS])]) + fi + if test "x$with_wayland" != "xno" then WINE_PACKAGE_FLAGS(WAYLAND_CLIENT,[wayland-client],,,, @@ -1443,8 +1449,6 @@ then [WAYLAND_CLIENT_LIBS=""],[$WAYLAND_CLIENT_LIBS])])]) WINE_PACKAGE_FLAGS(XKBCOMMON,[xkbcommon],,,, [AC_CHECK_LIB(xkbcommon,xkb_context_new,[:],[XKBCOMMON_LIBS=""],[$XKBCOMMON_LIBS])]) - WINE_PACKAGE_FLAGS(XKBREGISTRY,[xkbregistry],,,, - [AC_CHECK_LIB(xkbregistry,rxkb_context_new,[:],[XKBREGISTRY_LIBS=""],[$XKBREGISTRY_LIBS])]) if test "x$with_opengl" != "xno" then WINE_PACKAGE_FLAGS(WAYLAND_EGL,[wayland-egl],,,, diff --git a/dlls/winex11.drv/Makefile.in b/dlls/winex11.drv/Makefile.in index f9ddd05175b..31768e670da 100644 --- a/dlls/winex11.drv/Makefile.in +++ b/dlls/winex11.drv/Makefile.in @@ -1,7 +1,7 @@ MODULE = winex11.drv UNIXLIB = winex11.so -UNIX_CFLAGS = $(X_CFLAGS) -UNIX_LIBS = -lwin32u $(X_LIBS) $(PTHREAD_LIBS) -lm +UNIX_CFLAGS = $(X_CFLAGS) $(XKBREGISTRY_CFLAGS) +UNIX_LIBS = -lwin32u $(X_LIBS) $(XKBREGISTRY_LIBS) $(PTHREAD_LIBS) -lm VER_FILEDESCRIPTION_STR = "Wine X11 driver" diff --git a/dlls/winex11.drv/keyboard.c b/dlls/winex11.drv/keyboard.c index 898286a5e6e..73c605b71ba 100644 --- a/dlls/winex11.drv/keyboard.c +++ b/dlls/winex11.drv/keyboard.c @@ -36,6 +36,10 @@ #include <X11/Xutil.h> #include <X11/XKBlib.h> +#ifdef SONAME_LIBXKBREGISTRY +#include <xkbcommon/xkbregistry.h> +#endif + #include <ctype.h> #include <stdarg.h> #include <string.h> @@ -939,6 +943,9 @@ static const struct { {0, NULL, NULL, NULL, NULL} /* sentinel */ }; static unsigned kbd_layout=0; /* index into above table of layouts */ +#ifdef SONAME_LIBXKBREGISTRY +static struct rxkb_context *rxkb_context; +#endif /* maybe more of these scancodes should be extended? */ /* extended must be set for ALT_R, CTRL_R, @@ -1426,6 +1433,152 @@ BOOL X11DRV_KeyEvent( HWND hwnd, XEvent *xev ) return TRUE; } +#ifdef SONAME_LIBXKBREGISTRY +static BOOL find_xkb_layout_variant(const char *name, const char **layout, const char **variant) +{ + struct rxkb_layout *iter; + + for (iter = rxkb_layout_first(rxkb_context); iter; iter = rxkb_layout_next(iter)) + { + const char *desc = rxkb_layout_get_description(iter); + + if (desc && !strcmp(name, desc)) + { + *layout = rxkb_layout_get_name(iter); + *variant = rxkb_layout_get_variant(iter); + return TRUE; + } + } + + return FALSE; +} + +static const struct layout_id_map_entry +{ + const char *name; + LANGID langid; +} layout_ids[] = +{ + { "af", MAKELANGID(LANG_DARI, SUBLANG_DEFAULT) }, + { "al", MAKELANGID(LANG_ALBANIAN, SUBLANG_DEFAULT) }, + { "am", MAKELANGID(LANG_ARMENIAN, SUBLANG_DEFAULT) }, + { "ara", MAKELANGID(LANG_ARABIC, SUBLANG_DEFAULT) }, + { "at", MAKELANGID(LANG_GERMAN, SUBLANG_GERMAN_AUSTRIAN) }, + { "az", MAKELANGID(LANG_AZERBAIJANI, SUBLANG_DEFAULT) }, + { "au", MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_AUS) }, + { "ba", MAKELANGID(LANG_BOSNIAN, SUBLANG_BOSNIAN_BOSNIA_HERZEGOVINA_CYRILLIC) }, + { "bd", MAKELANGID(LANG_BANGLA, SUBLANG_DEFAULT) }, + { "be", MAKELANGID(LANG_FRENCH, SUBLANG_FRENCH_BELGIAN) }, + { "bg", MAKELANGID(LANG_BULGARIAN, SUBLANG_DEFAULT) }, + { "br", MAKELANGID(LANG_PORTUGUESE, 2) }, + { "brai", MAKELANGID(LANG_NEUTRAL, SUBLANG_CUSTOM_DEFAULT) }, + { "bt", MAKELANGID(LANG_TIBETAN, 3) }, + { "bw", MAKELANGID(LANG_TSWANA, SUBLANG_TSWANA_BOTSWANA) }, + { "by", MAKELANGID(LANG_BELARUSIAN, SUBLANG_DEFAULT) }, + { "ca", MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_CAN) }, + { "cd", MAKELANGID(LANG_FRENCH, SUBLANG_CUSTOM_UNSPECIFIED) }, + { "ch", MAKELANGID(LANG_GERMAN, SUBLANG_GERMAN_SWISS) }, + { "cm", MAKELANGID(LANG_FRENCH, 11) }, + { "cn", MAKELANGID(LANG_CHINESE, SUBLANG_DEFAULT) }, + { "cz", MAKELANGID(LANG_CZECH, SUBLANG_DEFAULT) }, + { "de", MAKELANGID(LANG_GERMAN, SUBLANG_DEFAULT) }, + { "dk", MAKELANGID(LANG_DANISH, SUBLANG_DEFAULT) }, + { "dz", MAKELANGID(LANG_TAMAZIGHT, SUBLANG_TAMAZIGHT_ALGERIA_LATIN) }, + { "ee", MAKELANGID(LANG_ESTONIAN, SUBLANG_DEFAULT) }, + { "epo", MAKELANGID(LANG_NEUTRAL, SUBLANG_CUSTOM_DEFAULT) }, + { "es", MAKELANGID(LANG_SPANISH, SUBLANG_DEFAULT) }, + { "et", MAKELANGID(LANG_AMHARIC, SUBLANG_DEFAULT) }, + { "fi", MAKELANGID(LANG_FINNISH, SUBLANG_DEFAULT) }, + { "fo", MAKELANGID(LANG_FAEROESE, SUBLANG_DEFAULT) }, + { "fr", MAKELANGID(LANG_FRENCH, SUBLANG_DEFAULT) }, + { "gb", MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_UK) }, + { "ge", MAKELANGID(LANG_GEORGIAN, SUBLANG_DEFAULT) }, + { "gh", MAKELANGID(LANG_ENGLISH, SUBLANG_CUSTOM_UNSPECIFIED) }, + { "gn", MAKELANGID(LANG_NEUTRAL, SUBLANG_CUSTOM_DEFAULT) }, + { "gr", MAKELANGID(LANG_GREEK, SUBLANG_DEFAULT) }, + { "hr", MAKELANGID(LANG_CROATIAN, SUBLANG_DEFAULT) }, + { "hu", MAKELANGID(LANG_HUNGARIAN, SUBLANG_DEFAULT) }, + { "id", MAKELANGID(LANG_INDONESIAN, SUBLANG_DEFAULT) }, + { "ie", MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_EIRE) }, + { "il", MAKELANGID(LANG_HEBREW, SUBLANG_DEFAULT) }, + { "in", MAKELANGID(LANG_HINDI, SUBLANG_DEFAULT) }, + { "iq", MAKELANGID(LANG_ARABIC, SUBLANG_ARABIC_IRAQ) }, + { "ir", MAKELANGID(LANG_PERSIAN, SUBLANG_DEFAULT) }, + { "is", MAKELANGID(LANG_ICELANDIC, SUBLANG_DEFAULT) }, + { "it", MAKELANGID(LANG_ITALIAN, SUBLANG_DEFAULT) }, + { "jp", MAKELANGID(LANG_JAPANESE, SUBLANG_DEFAULT) }, + { "ke", MAKELANGID(LANG_NEUTRAL, SUBLANG_CUSTOM_DEFAULT) }, + { "kg", MAKELANGID(LANG_KYRGYZ, SUBLANG_DEFAULT) }, + { "kh", MAKELANGID(LANG_KHMER, SUBLANG_DEFAULT) }, + { "kr", MAKELANGID(LANG_KOREAN, SUBLANG_DEFAULT) }, + { "kz", MAKELANGID(LANG_KAZAK, SUBLANG_DEFAULT) }, + { "la", MAKELANGID(LANG_LAO, SUBLANG_DEFAULT) }, + { "latam", MAKELANGID(LANG_SPANISH, SUBLANG_CUSTOM_UNSPECIFIED) }, + { "lk", MAKELANGID(LANG_SINHALESE, SUBLANG_DEFAULT) }, + { "lt", MAKELANGID(LANG_LITHUANIAN, SUBLANG_DEFAULT) }, + { "lv", MAKELANGID(LANG_LATVIAN, SUBLANG_DEFAULT) }, + { "ma", MAKELANGID(LANG_ARABIC, SUBLANG_ARABIC_MOROCCO) }, + { "mao", MAKELANGID(LANG_MAORI, SUBLANG_DEFAULT) }, + { "md", MAKELANGID(LANG_ROMANIAN, SUBLANG_CUSTOM_UNSPECIFIED) }, + { "me", MAKELANGID(LANG_SERBIAN, SUBLANG_SERBIAN_MONTENEGRO_LATIN) }, + { "mk", MAKELANGID(LANG_MACEDONIAN, SUBLANG_DEFAULT) }, + { "ml", MAKELANGID(LANG_NEUTRAL, SUBLANG_CUSTOM_DEFAULT) }, + { "mm", MAKELANGID(0x55 /*LANG_BURMESE*/, SUBLANG_DEFAULT) }, + { "mn", MAKELANGID(LANG_MONGOLIAN, SUBLANG_DEFAULT) }, + { "mt", MAKELANGID(LANG_MALTESE, SUBLANG_DEFAULT) }, + { "mv", MAKELANGID(LANG_DIVEHI, SUBLANG_DEFAULT) }, + { "my", MAKELANGID(LANG_MALAY, SUBLANG_DEFAULT) }, + { "ng", MAKELANGID(LANG_ENGLISH, SUBLANG_CUSTOM_UNSPECIFIED) }, + { "nl", MAKELANGID(LANG_DUTCH, SUBLANG_DEFAULT) }, + { "no", MAKELANGID(LANG_NORWEGIAN, SUBLANG_DEFAULT) }, + { "np", MAKELANGID(LANG_NEPALI, SUBLANG_DEFAULT) }, + { "ph", MAKELANGID(LANG_FILIPINO, SUBLANG_DEFAULT) }, + { "pk", MAKELANGID(LANG_URDU, SUBLANG_DEFAULT) }, + { "pl", MAKELANGID(LANG_POLISH, SUBLANG_DEFAULT) }, + { "pt", MAKELANGID(LANG_PORTUGUESE, SUBLANG_DEFAULT) }, + { "ro", MAKELANGID(LANG_ROMANIAN, SUBLANG_DEFAULT) }, + { "rs", MAKELANGID(LANG_SERBIAN, SUBLANG_SERBIAN_LATIN) }, + { "ru", MAKELANGID(LANG_RUSSIAN, SUBLANG_DEFAULT) }, + { "se", MAKELANGID(LANG_SWEDISH, SUBLANG_DEFAULT) }, + { "si", MAKELANGID(LANG_SLOVENIAN, SUBLANG_DEFAULT) }, + { "sk", MAKELANGID(LANG_SLOVAK, SUBLANG_DEFAULT) }, + { "sn", MAKELANGID(LANG_WOLOF, SUBLANG_DEFAULT) }, + { "sy", MAKELANGID(LANG_SYRIAC, SUBLANG_DEFAULT) }, + { "tg", MAKELANGID(LANG_FRENCH, SUBLANG_CUSTOM_UNSPECIFIED) }, + { "th", MAKELANGID(LANG_THAI, SUBLANG_DEFAULT) }, + { "tj", MAKELANGID(LANG_TAJIK, SUBLANG_DEFAULT) }, + { "tm", MAKELANGID(LANG_TURKMEN, SUBLANG_DEFAULT) }, + { "tr", MAKELANGID(LANG_TURKISH, SUBLANG_DEFAULT) }, + { "tw", MAKELANGID(LANG_CHINESE, SUBLANG_CUSTOM_UNSPECIFIED) }, + { "tz", MAKELANGID(LANG_SWAHILI, SUBLANG_CUSTOM_UNSPECIFIED) }, + { "ua", MAKELANGID(LANG_UKRAINIAN, SUBLANG_DEFAULT) }, + { "us", MAKELANGID(LANG_ENGLISH, SUBLANG_DEFAULT) }, + { "uz", MAKELANGID(LANG_UZBEK, 2) }, + { "vn", MAKELANGID(LANG_VIETNAMESE, SUBLANG_DEFAULT) }, + { "za", MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_SOUTH_AFRICA) }, +}; + +static int xkb_layout_cmp(const void *key, const void *element) +{ + const struct layout_id_map_entry *entry = element; + + return strcmp(key, entry->name); +} + +static LANGID x11drv_langid_from_xkb_layout(const char *layout) +{ + struct layout_id_map_entry *entry; + + entry = bsearch(layout, layout_ids, ARRAY_SIZE(layout_ids), sizeof(*layout_ids), xkb_layout_cmp); + if (entry) + return entry->langid; + + FIXME("Unknown layout language %s\n", debugstr_a(layout)); + return MAKELANGID(LANG_NEUTRAL, SUBLANG_CUSTOM_UNSPECIFIED); +}; + +#endif + /********************************************************************** * X11DRV_KEYBOARD_DetectLayout * @@ -1808,9 +1961,14 @@ void X11DRV_InitKeyboard( Display *display ) XModifierKeymap *mmp; KeyCode *kcp; XkbStateRec xkb_state; - char *xkb_group_names[4]; - unsigned int i, xkb_group, xkb_group_count; +#ifdef SONAME_LIBXKBREGISTRY + const char *layout, *variant = NULL; + char *xkb_group_names[4], *name; + unsigned int xkb_group_count; +#endif XkbDescRec *xkb_desc; + unsigned int i, xkb_group; + LANGID lang = 0; Status s; pthread_mutex_lock( &kbd_mutex ); @@ -1852,6 +2010,7 @@ void X11DRV_InitKeyboard( Display *display ) xkb_desc = XkbGetMap( display, XkbAllClientInfoMask, XkbUseCoreKbd ); if (xkb_desc) { +#ifdef SONAME_LIBXKBREGISTRY XkbGetNames( display, XkbGroupNamesMask, xkb_desc ); for (i = 0; i < ARRAY_SIZE(xkb_desc->names->groups); i++) { @@ -1862,16 +2021,24 @@ void X11DRV_InitKeyboard( Display *display ) if (!XGetAtomNames( display, xkb_desc->names->groups, xkb_group_count, xkb_group_names )) xkb_group_count = 0; for (i = 0; i < xkb_group_count; i++) - { TRACE("group %u name %s\n", i, debugstr_a(xkb_group_names[i])); + name = xkb_group_names[xkb_state.group]; + if (!name || !find_xkb_layout_variant(name, &layout, &variant)) layout = "us"; + TRACE("Found layout %u name %s -> %s:%s\n", xkb_group, name, layout, variant); + lang = x11drv_langid_from_xkb_layout(layout); + TRACE("lang %#x\n", lang); + for (i = 0; i < xkb_group_count; i++) XFree( xkb_group_names[i] ); - } +#endif XkbFreeKeyboard( xkb_desc, 0, True ); } X11DRV_KEYBOARD_DetectLayout( display, mmp, xkb_group ); XFreeModifiermap(mmp); + if (lang && lang != main_key_tab[kbd_layout].lcid) + WARN("LANGID from Xkb group name and LANGID detected by X11DRV_KEYBOARD_DetectLayout don't match!\n"); + x11drv_init_layout( display ); pthread_mutex_unlock( &kbd_mutex ); @@ -1913,6 +2080,19 @@ BOOL X11DRV_MappingNotify( HWND dummy, XEvent *event ) return TRUE; } +void x11drv_xkb_init( Display *display ) +{ + XkbUseExtension( display, NULL, NULL ); + +#ifdef SONAME_LIBXKBREGISTRY + if (!(rxkb_context = rxkb_context_new(RXKB_CONTEXT_NO_FLAGS)) + || !rxkb_context_parse_default_ruleset(rxkb_context)) + { + ERR("Failed to parse default Xkb ruleset\n"); + return; + } +#endif +} /*********************************************************************** * VkKeyScanEx (X11DRV.@) diff --git a/dlls/winex11.drv/x11drv.h b/dlls/winex11.drv/x11drv.h index 8b0c0a62f91..98bc4fd8ecc 100644 --- a/dlls/winex11.drv/x11drv.h +++ b/dlls/winex11.drv/x11drv.h @@ -755,6 +755,7 @@ extern void ungrab_clipping_window(void); extern void move_resize_window( HWND hwnd, int dir, POINT pos ); extern void X11DRV_InitKeyboard( Display *display ); extern BOOL X11DRV_ProcessEvents( DWORD mask ); +extern void x11drv_xkb_init( Display *display ); 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 1bdcd282f2e..556057295a8 100644 --- a/dlls/winex11.drv/x11drv_main.c +++ b/dlls/winex11.drv/x11drv_main.c @@ -672,7 +672,7 @@ NTSTATUS __wine_unix_lib_init(void) #endif x11drv_xinput2_load(); - XkbUseExtension( gdi_display, NULL, NULL ); + x11drv_xkb_init( gdi_display ); X11DRV_InitKeyboard( gdi_display ); if (use_xim) use_xim = xim_init( input_style ); diff --git a/include/config.h.in b/include/config.h.in index 0343acc8d36..551771eaa6a 100644 --- a/include/config.h.in +++ b/include/config.h.in @@ -810,6 +810,9 @@ /* Define to the soname of the libXinerama library. */ #undef SONAME_LIBXINERAMA +/* Define to the soname of the libxkbregistry library. */ +#undef SONAME_LIBXKBREGISTRY + /* Define to the soname of the libXrandr library. */ #undef SONAME_LIBXRANDR -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10550
On Thu Apr 16 07:30:08 2026 +0000, Rémi Bernon wrote:
I'm not entirely sure about it but as you prefer. My reluctance is mostly about diverging from winewayland. I had hopes that large parts of both keyboard layout handling could eventually be factored together, possibly in win32u though I'm not yet sure how. It's not too much of a diverging but lets try to be conservative if possible (or change winewayland accordingly). Oh, alright; I'll keep that in mind and try to reduce unnecessary differences.
-- https://gitlab.winehq.org/wine/wine/-/merge_requests/10550#note_136537
On Thu Apr 16 15:35:46 2026 +0000, Rémi Bernon wrote:
IIUC from XkbFreeKeyboard documentation, with free_all = True everything gets freed, so there's no need to have separate calls. I had planned to look this up but then forgot :/ Yeah, libX11 sources confirm that `XkbFreeKeyboard()` will free everything. Thanks!
-- https://gitlab.winehq.org/wine/wine/-/merge_requests/10550#note_136538
On Thu Apr 16 15:34:35 2026 +0000, Matteo Bruni wrote:
changed this line in [version 4 of the diff](/wine/wine/-/merge_requests/10550/diffs?diff_id=260864&start_sha=a27a5af9d3bcc63c6fc65427e8800b48c8bca06c#87db583be5c13c1f7b3c958b10e03d67b6a2ca06_1453_1452) Done. I guess it could even be made unconditional.
-- https://gitlab.winehq.org/wine/wine/-/merge_requests/10550#note_136539
I opened https://gitlab.winehq.org/wine/wine/-/merge_requests/10689 with some misc additional changes, please have a look and iterate from them, or approve if you agree. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/10550#note_136686
participants (3)
-
Matteo Bruni -
Matteo Bruni (@Mystral) -
Rémi Bernon (@rbernon)