[PATCH 0/5] 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 This first MR does very little, just testing the waters... -- https://gitlab.winehq.org/wine/wine/-/merge_requests/10550
From: Matteo Bruni <mbruni@codeweavers.com> --- dlls/winex11.drv/keyboard.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/dlls/winex11.drv/keyboard.c b/dlls/winex11.drv/keyboard.c index e2281bef017..82faa5741be 100644 --- a/dlls/winex11.drv/keyboard.c +++ b/dlls/winex11.drv/keyboard.c @@ -939,6 +939,7 @@ static const struct { {0, NULL, NULL, NULL, NULL} /* sentinel */ }; static unsigned kbd_layout=0; /* index into above table of layouts */ +static XkbDescRec *xkb_desc; /* maybe more of these scancodes should be extended? */ /* extended must be set for ALT_R, CTRL_R, @@ -1611,6 +1612,18 @@ void X11DRV_InitKeyboard( Display *display ) } XFreeModifiermap(mmp); + if (xkb_desc) + { + XkbFreeClientMap( xkb_desc, 0, True ); + XkbFreeKeyboard( xkb_desc, XkbNamesMask, True ); + xkb_desc = NULL; + } + + TRACE("min_keycode %u, max_keycode %u\n", min_keycode, max_keycode); + xkb_desc = XkbGetMap( display, XkbAllClientInfoMask, XkbUseCoreKbd ); + if (xkb_desc) + TRACE("min_key_code %u, max_key_code %u\n", xkb_desc->min_key_code, xkb_desc->max_key_code); + /* 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> --- dlls/winex11.drv/keyboard.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/dlls/winex11.drv/keyboard.c b/dlls/winex11.drv/keyboard.c index 82faa5741be..2a8570fafb3 100644 --- a/dlls/winex11.drv/keyboard.c +++ b/dlls/winex11.drv/keyboard.c @@ -940,6 +940,7 @@ static const struct { }; static unsigned kbd_layout=0; /* index into above table of layouts */ static XkbDescRec *xkb_desc; +static unsigned int xkb_current_group; /* maybe more of these scancodes should be extended? */ /* extended must be set for ALT_R, CTRL_R, @@ -1562,6 +1563,7 @@ 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; /* Ranges of OEM, function key, and character virtual key codes. * Don't include those handled specially in X11DRV_ToUnicodeEx and @@ -1624,6 +1626,10 @@ void X11DRV_InitKeyboard( Display *display ) if (xkb_desc) TRACE("min_key_code %u, max_key_code %u\n", xkb_desc->min_key_code, xkb_desc->max_key_code); + XkbGetState( display, XkbUseCoreKbd, &xkb_state ); + xkb_current_group = xkb_state.group; + TRACE("xkb_current_group %u\n", xkb_current_group); + /* 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> --- dlls/winex11.drv/keyboard.c | 26 +++++++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/dlls/winex11.drv/keyboard.c b/dlls/winex11.drv/keyboard.c index 2a8570fafb3..324a79f272d 100644 --- a/dlls/winex11.drv/keyboard.c +++ b/dlls/winex11.drv/keyboard.c @@ -1564,6 +1564,8 @@ void X11DRV_InitKeyboard( Display *display ) const char (*lkey)[MAIN_LEN][4]; char vkey_used[256] = { 0 }; XkbStateRec xkb_state; + char *group_names[4]; + unsigned int group_count = 0; /* Ranges of OEM, function key, and character virtual key codes. * Don't include those handled specially in X11DRV_ToUnicodeEx and @@ -1624,11 +1626,29 @@ void X11DRV_InitKeyboard( Display *display ) TRACE("min_keycode %u, max_keycode %u\n", min_keycode, max_keycode); xkb_desc = XkbGetMap( display, XkbAllClientInfoMask, XkbUseCoreKbd ); if (xkb_desc) + { TRACE("min_key_code %u, max_key_code %u\n", xkb_desc->min_key_code, xkb_desc->max_key_code); - XkbGetState( display, XkbUseCoreKbd, &xkb_state ); - xkb_current_group = xkb_state.group; - TRACE("xkb_current_group %u\n", xkb_current_group); + XkbGetState( display, XkbUseCoreKbd, &xkb_state ); + xkb_current_group = xkb_state.group; + TRACE("xkb_current_group %u\n", xkb_current_group); + + XkbGetNames( display, XkbGroupNamesMask, xkb_desc ); + for (i = 0; i < ARRAY_SIZE(xkb_desc->names->groups); i++) + { + if (!xkb_desc->names->groups[i]) + break; + } + group_count = i; + if (!XGetAtomNames( display, xkb_desc->names->groups, group_count, group_names )) + group_count = 0; + for (i = 0; i < group_count; i++) + { + TRACE("groups[%u] name %s\n", i, group_names[i]); + XFree( group_names[i] ); + } + XkbFreeNames( xkb_desc, XkbGroupNamesMask, True ); + } /* Detect the keyboard layout */ X11DRV_KEYBOARD_DetectLayout( display ); -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10550
From: Matteo Bruni <mbruni@codeweavers.com> --- dlls/winex11.drv/keyboard.c | 10 ++++++++++ dlls/winex11.drv/x11drv.h | 2 ++ dlls/winex11.drv/x11drv_main.c | 5 ++--- 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/dlls/winex11.drv/keyboard.c b/dlls/winex11.drv/keyboard.c index 324a79f272d..1c261f66efa 100644 --- a/dlls/winex11.drv/keyboard.c +++ b/dlls/winex11.drv/keyboard.c @@ -1892,6 +1892,16 @@ BOOL X11DRV_MappingNotify( HWND dummy, XEvent *event ) return TRUE; } +void x11drv_xkb_init( Display *display ) +{ + XkbUseExtension( display, NULL, NULL ); +} + +void x11drv_xkb_init_thread( struct x11drv_thread_data *data ) +{ + XkbUseExtension( data->display, NULL, NULL ); + XkbSetDetectableAutoRepeat( data->display, True, NULL ); +} /*********************************************************************** * VkKeyScanEx (X11DRV.@) diff --git a/dlls/winex11.drv/x11drv.h b/dlls/winex11.drv/x11drv.h index 9f38ceba0df..0a4f328c488 100644 --- a/dlls/winex11.drv/x11drv.h +++ b/dlls/winex11.drv/x11drv.h @@ -754,6 +754,8 @@ 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 ); +extern void x11drv_xkb_init_thread( struct x11drv_thread_data *data ); 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..3198ee5a5da 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 ); @@ -751,8 +751,7 @@ struct x11drv_thread_data *x11drv_init_thread_data(void) fcntl( ConnectionNumber(data->display), F_SETFD, 1 ); /* set close on exec flag */ - XkbUseExtension( data->display, NULL, NULL ); - XkbSetDetectableAutoRepeat( data->display, True, NULL ); + x11drv_xkb_init_thread( data ); if (TRACE_ON(synchronous)) XSynchronize( data->display, True ); set_queue_display_fd( data->display ); -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10550
From: Matteo Bruni <mbruni@codeweavers.com> --- dlls/winex11.drv/keyboard.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/dlls/winex11.drv/keyboard.c b/dlls/winex11.drv/keyboard.c index 1c261f66efa..e3f8d6912ec 100644 --- a/dlls/winex11.drv/keyboard.c +++ b/dlls/winex11.drv/keyboard.c @@ -942,6 +942,8 @@ static unsigned kbd_layout=0; /* index into above table of layouts */ static XkbDescRec *xkb_desc; static unsigned int xkb_current_group; +static int xkb_event_base, xkb_error_base; + /* maybe more of these scancodes should be extended? */ /* extended must be set for ALT_R, CTRL_R, INS, DEL, HOME, END, PAGE_UP, PAGE_DOWN, ARROW keys, @@ -1895,6 +1897,8 @@ BOOL X11DRV_MappingNotify( HWND dummy, XEvent *event ) void x11drv_xkb_init( Display *display ) { XkbUseExtension( display, NULL, NULL ); + XkbQueryExtension( display, 0, &xkb_event_base, &xkb_error_base, 0, 0 ); + TRACE("xkb_event_base %u, xkb_error_base %u\n", xkb_event_base, xkb_error_base); } void x11drv_xkb_init_thread( struct x11drv_thread_data *data ) -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10550
Rémi Bernon (@rbernon) commented about dlls/winex11.drv/keyboard.c:
{0, NULL, NULL, NULL, NULL} /* sentinel */ }; static unsigned kbd_layout=0; /* index into above table of layouts */ +static XkbDescRec *xkb_desc; I don't think there's any justification to make it static.
-- https://gitlab.winehq.org/wine/wine/-/merge_requests/10550#note_135269
Rémi Bernon (@rbernon) commented about dlls/winex11.drv/keyboard.c:
void x11drv_xkb_init( Display *display ) { XkbUseExtension( display, NULL, NULL ); + XkbQueryExtension( display, 0, &xkb_event_base, &xkb_error_base, 0, 0 ); + TRACE("xkb_event_base %u, xkb_error_base %u\n", xkb_event_base, xkb_error_base); Do we really need to use Xkb events? I believe that every time keyboard description (ie: the set of active groups) changes, a MappingNotify will be sent, which we already listen for. Isn't that enough to refresh the group description?
-- https://gitlab.winehq.org/wine/wine/-/merge_requests/10550#note_135271
Rémi Bernon (@rbernon) commented about dlls/winex11.drv/keyboard.c:
}; static unsigned kbd_layout=0; /* index into above table of layouts */ static XkbDescRec *xkb_desc; +static unsigned int xkb_current_group; Same thing here, and looking at the whole branch it seems to be gone at some point, so this doesn't look very useful. In general I believe the current xkb group is just part of the key event state, and doesn't really need to be tracked separately?
-- https://gitlab.winehq.org/wine/wine/-/merge_requests/10550#note_135270
Also this doesn't seem to do anything very useful, maybe we can squeeze these with something more? -- https://gitlab.winehq.org/wine/wine/-/merge_requests/10550#note_135273
On Tue Apr 7 08:32:02 2026 +0000, Rémi Bernon wrote:
I don't think there's any justification to make it static. My general plan was to introduce the "new stuff" very gradually and making use of the fact that some global state already exists (e.g. `kbd_layout`) to add more to that, if temporarily. Later on I'd strip out questionable old and new global per-process state.
I can reorder / rework things so that I avoid adding further per-process state from the start. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/10550#note_135321
On Tue Apr 7 08:50:40 2026 +0000, Rémi Bernon wrote:
Do we really need to use Xkb events? I believe that every time keyboard description (ie: the set of active groups) changes, a MappingNotify will be sent, which we already listen for. Isn't that enough to refresh the groups descriptions? I see that I probably "broke" the MappingNotify events by enabling the Xkb ones: https://www.x.org/releases/X11R7.6/doc/libX11/specs/XKB/xkblib.html#effects_...
Since I wasn't getting any MappingNotify events I rebuild that instrumentation around the Xkb events. I'll get rid of that and see how well it works with just MappingNotify. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/10550#note_135322
On Tue Apr 7 08:32:03 2026 +0000, Rémi Bernon wrote:
Same thing here, and looking at the whole branch it seems to be gone at some point, so this doesn't look very useful. In general I believe the current xkb group is just part of the key event state, and doesn't really need to be tracked separately? We could get the Xkb group from the `KeyEvent`, in a slightly obscure way (i.e. probably something like `(state >> 0x2000) & 0x3`). Potential downsides: I'm not 100% sure how safe that is (probably pretty safe) and waiting for a KeyEvent can mean delaying updating the internal state until the next keypress.
In the branch I've been using the `XkbStateNotify` event to detect the X11 layout switch: https://gitlab.winehq.org/Mystral/wine/-/commit/59279a0a668cf5d27bca923db3b1.... Eventually that directly triggers a win32 layout switch as well, without needing to store the current group id in global state. I'll check if that works well enough via the plain old `MappingNotify` event. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/10550#note_135323
On Tue Apr 7 18:35:05 2026 +0000, Rémi Bernon wrote:
Also this doesn't seem to do anything very useful, maybe we can squeeze these with something more? Fair enough, I'll merge some more changes into it next time I push.
-- https://gitlab.winehq.org/wine/wine/-/merge_requests/10550#note_135325
Thanks for the review! One general question: does the general approach / order seem reasonable? For a very high level view: - introduce a tiny bit more smarts into the old `X11DRV_KEYBOARD_DetectLayout()`[https://gitlab.winehq.org/Mystral/wine/-/commit/7ef5e7436f5b0a689d152aeedd22...] - introduce `struct layout` and store the `LANGID`s detected via xkbregistry, e.g. [https://gitlab.winehq.org/Mystral/wine/-/commit/b35f3171256d0c96d756506b1f66...] - add fallback `struct layout` entries if the Xkb + xkbregistry route fails [https://gitlab.winehq.org/Mystral/wine/-/commit/e96172e24c8c9c9b92360a76756e...] - always use `struct layout` for keyboard layout stuff, e.g. [https://gitlab.winehq.org/Mystral/wine/-/commit/6f3a9d4708c13f9a2749b5ae511d...] -- https://gitlab.winehq.org/wine/wine/-/merge_requests/10550#note_135326
On Tue Apr 7 18:35:04 2026 +0000, Matteo Bruni wrote:
My general plan was to introduce the "new stuff" very gradually and making use of the fact that some global state already exists (e.g. `kbd_layout`) to add more to that, if temporarily. Later on I'd strip out questionable old and new global per-process state. I can reorder / rework things so that I avoid adding further per-process state from the start. Well okay, but as far as I can see you only need xkb_desc for the layout detection, which only ever happens on initialization and whenever the set of active Xkb groups changes. In both cases you need to recreate it, so it can also be destroyed once done with it?
-- https://gitlab.winehq.org/wine/wine/-/merge_requests/10550#note_135350
On Tue Apr 7 21:17:37 2026 +0000, Matteo Bruni wrote:
Thanks for the review! One general question: does the general approach / order seem reasonable? For a very high level view: - introduce a tiny bit more smarts into the old `X11DRV_KEYBOARD_DetectLayout()`[https://gitlab.winehq.org/Mystral/wine/-/commit/7ef5e7436f5b0a689d152aeedd22...] - introduce `struct layout` and store the `LANGID`s detected via xkbregistry, e.g. [https://gitlab.winehq.org/Mystral/wine/-/commit/b35f3171256d0c96d756506b1f66...] - add fallback `struct layout` entries if the Xkb + xkbregistry route fails [https://gitlab.winehq.org/Mystral/wine/-/commit/e96172e24c8c9c9b92360a76756e...] - always use `struct layout` for keyboard layout stuff, e.g. [https://gitlab.winehq.org/Mystral/wine/-/commit/6f3a9d4708c13f9a2749b5ae511d...] Well, I don't know about modifying the current code, or even about trying to shoehorn it into the new KBDTABLE mechanism. I would personally just leave it to die, and introduce the new proper layout detection, until a point where we can switch over to using it by default, possibly guarded with some registry configuration option if we're still still unsure about it. This way we can decide to keep it or ditch it depending on actual reported use cases, possibly ditch it after fixing the issues with the new mechanism.
I know I mentioned something about VNC before, but after checking again the issue isn't so much about layout detection and I think things should work as well (or as badly) as they are now. The detection is broken with VNC already with the current code, this is because VNC doesn't pass through keyboard layout information and simply exposes a server-side configured XKB layout to the applications. By default this is a single US keyboard, but it can be changed with setxkbmap as well. When VNC receives a key even, it remaps the keycode to whichever keycode would have generate that keysym with its layout. This means that if you have a FR client layout, and an US server layout, Wine will detect the layout as US too. The keycodes sent from the client would be the FR keycode, but they would be remapped by the XVNC server to match the keysym, and Wine would use keycode / keysym matching an US layout. As they are remapped by the XVNC server keysym would still correctly match whatever the user is typing. The only issues would be about key positions, for any application that relies on the keyboard physical layout. Note that this issue already exists with the current code, it is a problem coming from the VNC layout remapping and not something Wine can do anything about. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/10550#note_135352
On Tue Apr 7 18:35:05 2026 +0000, Matteo Bruni wrote:
We could get the Xkb group from the `KeyEvent`, in a slightly obscure way (i.e. probably something like `(state >> 0x2000) & 0x3`). Potential downsides: I'm not 100% sure how safe that is (probably pretty safe) and waiting for a KeyEvent can mean delaying updating the internal state until the next keypress. In the branch I've been using the `XkbStateNotify` event to detect the X11 layout switch: https://gitlab.winehq.org/Mystral/wine/-/commit/59279a0a668cf5d27bca923db3b1.... Eventually that directly triggers a win32 layout switch as well, without needing to store the current group id in global state. I'll check if that works well enough via the plain old `MappingNotify` event. I think the less we rely on X11 extensions the safer it is. I have no idea how well supported / implemented the Xkb extension is, and if this can work with only standard X11 events it may be better?
The group in the KeyEvent state bits is defined in the Xkb specification and should be reliable (and if Xkb isn't available they should either be 0, or we should ignore them): ``` Normally, the Xkb-aware server reports keyboard state in the state member of events such as a KeyPress event and ButtonPress event, encoded as follows: bits meaning 15 0 13-14 Group index 8-12 Pointer Buttons 0-7 Modifiers For Xkb-unaware clients, only core protocol keyboard information may be reported. Because core protocol does not define the group index, the group index is mapped to mod- ifier bits as specified by the groups[group index] field of the compatibility map (the bits set in the compatibility map are ORed into bits 0-7 of the state), and bits 13-14 are reported in the event as zero. ``` -- https://gitlab.winehq.org/wine/wine/-/merge_requests/10550#note_135351
On Thu Apr 9 11:12:16 2026 +0000, Matteo Bruni wrote:
I see that I probably "broke" the MappingNotify events by enabling the Xkb ones: https://www.x.org/releases/X11R7.6/doc/libX11/specs/XKB/xkblib.html#effects_... Since I wasn't getting any MappingNotify event I rebuilt that instrumentation around the Xkb events. I'll get rid of that and see how well it works with just MappingNotify. It looks like I don't get any MappingNotify events already with upstream Wine. That's on both pure X11 and Xwayland. Thinking back to it, it was probably the other way around: I saw no MappingNotify event, interpreted that Xkblib doc excerpt as "if you explicitly use Xkb you don't get MappingNotify events" and proceeded to enable and handle the Xkb events instead.
I'm not sure what broke this and how far back it goes. For what it's worth, Xkb events seem to work pretty okay in the setups I checked and we could always add more failsafes if necessary (e.g. in your draft MR you update the current Xkb group from `KeyEvent`, https://gitlab.winehq.org/wine/wine/-/merge_requests/2122/diffs?commit_id=8c...) -- https://gitlab.winehq.org/wine/wine/-/merge_requests/10550#note_135644
On Tue Apr 7 21:17:37 2026 +0000, Rémi Bernon wrote:
Well, I don't know about modifying the current code, or even about trying to shoehorn it into the new KBDTABLE mechanism. I would personally just leave it to die, and introduce the new proper layout detection, until a point where we can switch over to using it by default, possibly guarded with some registry configuration option if we're still still unsure about it. This way we can decide to keep it or ditch it depending on actual reported use cases, possibly ditch it after fixing the issues with the new mechanism. I know I mentioned something about VNC before, but after checking again the issue isn't so much about layout detection and I think things should work as well (or as badly) as they are now. The detection is broken with VNC already with the current code, this is because VNC doesn't pass through keyboard layout information and simply exposes a server-side configured XKB layout to the applications. By default this is a single US keyboard, but it can be changed with setxkbmap as well. When VNC receives a key even, it remaps the keycode to whichever keycode would have generate that keysym with its layout. This means that if you have a FR client layout, and an US server layout, Wine will detect the layout as US too. The keycodes sent from the client would be the FR keycode, but they would be remapped by the XVNC server to match the keysym, and Wine would use keycode / keysym matching an US layout. As they are remapped by the XVNC server keysym would still correctly match whatever the user is typing. The only issues would be about key positions, for any application that relies on the keyboard physical layout. Note that this issue already exists with the current code, it is a problem coming from the VNC layout remapping and not something Wine can do anything about. By the end of the branch, the only thing left of the old code is `X11DRV_KEYBOARD_DetectLayout()`, which eventually calls `create_layout_from_xkb()` thus creating a `struct layout` which contains the `KBDTABLE` filled exactly the same as via the xkbregistry code path. Effectively the only difference with xkb + registry should be where the layout and variant names come from. It doesn't seem to me like it's super ugly, but I'm sure we can disagree here :sweat_smile: I could certainly add a registry key to force the old vs new detection code temporarily, for testing regressions while we switch the default to the new one.
WRT vncserver, that's what I've seen as well. I'm only checking with that to make sure things don't get any worse. FWIW, for me tightvnc's vncserver crashes with current Wine because it doesn't support Xshape while Wine is using it unconditionally. I have one patch in the branch to fix that so that I can test vncserver. I haven't decided whether to upstream it or not: wondering if other people don't run into it for some reason or if it was a conscious decision to not handle the case where Xshape is supported at compile time but not at runtime. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/10550#note_135645
On Tue Apr 7 21:17:37 2026 +0000, Rémi Bernon wrote:
Well okay, but as far as I can see you only need xkb_desc for the layout detection, which only ever happens on initialization and whenever the set of active Xkb groups changes. In both cases you need to recreate it, so it can also be destroyed once done with it? Yes, it looks like it. I think I had different plans for the global `xkb_desc` originally, but now it can just be passed around as a function parameter.
-- https://gitlab.winehq.org/wine/wine/-/merge_requests/10550#note_135646
On Tue Apr 7 21:17:37 2026 +0000, Rémi Bernon wrote:
I think the less we rely on X11 extensions the safer it is. I have no idea how well supported / implemented the Xkb extension is, and if this can work with only standard X11 events it may be better? The group in the KeyEvent state bits is defined in the Xkb specification and should be reliable (and if Xkb isn't available they should either be 0, or we should ignore them): ``` Normally, the Xkb-aware server reports keyboard state in the state member of events such as a KeyPress event and ButtonPress event, encoded as follows: bits meaning 15 0 13-14 Group index 8-12 Pointer Buttons 0-7 Modifiers For Xkb-unaware clients, only core protocol keyboard information may be reported. Because core protocol does not define the group index, the group index is mapped to mod- ifier bits as specified by the groups[group index] field of the compatibility map (the bits set in the compatibility map are ORed into bits 0-7 of the state), and bits 13-14 are reported in the event as zero. ``` Ah, I had missed it. Thanks!
-- https://gitlab.winehq.org/wine/wine/-/merge_requests/10550#note_135647
On Thu Apr 9 11:14:26 2026 +0000, Matteo Bruni wrote:
It looks like I don't get any MappingNotify events already with upstream Wine. That's on both pure X11 and Xwayland. Thinking back to it, it was probably the other way around: I saw no MappingNotify event, interpreted that Xkblib doc excerpt as "if you explicitly use Xkb you don't get MappingNotify events" and proceeded to enable and handle the Xkb events instead. I'm not sure what broke this and how far back it goes. For what it's worth, Xkb events seem to work pretty okay in the setups I checked and we could always add more failsafes if necessary (e.g. in your draft MR you update the current Xkb group from `KeyEvent`, https://gitlab.winehq.org/wine/wine/-/merge_requests/2122/diffs?commit_id=8c...) I do see MappingNotify events, both on GNOME (Wayland) and KDE (X11 and Wayland). There are some variations between their behavior, some for instance reconfigure the keyboard every time the input method is changed, some only reconfigure the layout when switching between groups of 4-layouts. There are MappingNotify events when configuring the input methods as well.
There is some weird thing going on GNOME Wayland that we might need to figure out (or we'll have to delay layout detection), and it is that I only see MappingNotify events *after* a KeyEvent has been received, which means that if no key is pressed and the layout is switched to a different set of 4 layout groups (this is how GNOME manages the layouts), we might incorrectly use the layouts that were initialized on startup. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/10550#note_135658
On Thu Apr 9 12:46:09 2026 +0000, Rémi Bernon wrote:
I do see MappingNotify events, both on GNOME (Wayland) and KDE (X11 and Wayland). There are some variations between their behavior, some for instance reconfigure the keyboard every time the input method is changed, some only reconfigure the layout when switching between groups of 4-layouts. There are MappingNotify events when configuring the input methods as well. There is some weird thing going on GNOME Wayland that we might need to figure out (or we'll have to delay layout detection, or ignore the issue and consider it's a GNOME bug), and it is that I only see MappingNotify events *after* a KeyEvent has been received, which means that if no key is pressed and the layout is switched to a different set of 4 layout groups (this is how GNOME manages the layouts), we might incorrectly use the layouts that were initialized on startup. No idea why I'm not getting any MappingNotify event at all... I tried on Gnome X11 and Wayland, KDE X11, nothing. Some of that is in VMs but I don't see how that would make a difference. This is all with my own self-built Wine, usually with the new wow64 mode, but nothing particularly notable otherwise. I'm usually testing with my own small Windows test application, but same when using e.g. builtin notepad. Also tried to switch with the hotkeys vs with the mouse on the toolbar, no difference.
I'm attaching one of my logs, where I toggle the keyboard layout twice. This is with today's Wine, unchanged except I added a trace to `X11DRV_ProcessEvents()` for good measure... [log-notepad.txt.xz](/uploads/caff61076a7795736269345824dd0f7c/log-notepad.txt.xz) -- https://gitlab.winehq.org/wine/wine/-/merge_requests/10550#note_135698
On Thu Apr 9 14:55:23 2026 +0000, Matteo Bruni wrote:
No idea why I'm not getting any MappingNotify event at all... I tried on Gnome X11 and Wayland, KDE X11, nothing. Some of that is in VMs but I don't see how that would make a difference. This is all with my own self-built Wine, usually with the new wow64 mode, but nothing particularly notable otherwise. I'm usually testing with my own small Windows test application, but same when using e.g. builtin notepad. Also tried to switch with the hotkeys vs with the mouse on the toolbar, no difference. I'm attaching one of my logs, where I toggle the keyboard layout twice. This is with today's Wine, unchanged except I added a trace to `X11DRV_ProcessEvents()` for good measure... [log-notepad.txt.xz](/uploads/caff61076a7795736269345824dd0f7c/log-notepad.txt.xz) How many layouts have you configured? What does `setxkbmap -print` says before/after you switch?
Fwiw you can also check with `xev -event keyboard`, it should print the MappingNotify events whenever the Xkb descriptor changes, which happens for instance on GNOME, after you have pressed a key, iff you have more than 4 layouts configured, and when you switch from layout 0 to layout >= 4 for instance. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/10550#note_135702
On Thu Apr 9 15:13:56 2026 +0000, Rémi Bernon wrote:
How many layouts have you configured? What does `setxkbmap -print` says before/after you switch? Fwiw you can also check with `xev -event keyboard`, it should print the MappingNotify events whenever the Xkb descriptor changes, which happens for instance on GNOME, after you have pressed a key, iff you have more than 4 layouts configured, and when you switch from layout 0 to layout
= 4 for instance. Ahhh, yes, that was it. The keymap in use initially[*] covers multiple layouts and, depending on the specific transition, it could handle the new layout without switching the underlying keymap. For the records the log was taken with KDE while switching between "us" and "it" layouts (layout 0 and 1 respectively). If I switch to "ru" or "jp" (which are layouts 2 and 3) I get the MappingNotify event.
This brings up the "not noticing the layout switch until the next keypress" problem though. I don't know how important it is but it might be a point in favor of listening to Xkb events when available. [*] it's described like this: `xkb_symbols { include "pc+us+it:2+inet(evdev)" };` -- https://gitlab.winehq.org/wine/wine/-/merge_requests/10550#note_135704
This brings up the "not noticing the layout switch until the next keypress" problem though. I don't know how important it is but it might be a point in favor of listening to Xkb events when available.
This seems to be a GNOME specific problem, possibly their bug, and maybe we don't have to worry about it for now. Also, I'm not sure that using Xkb events would make any difference, have you checked that it does? -- https://gitlab.winehq.org/wine/wine/-/merge_requests/10550#note_135705
On Thu Apr 9 15:51:07 2026 +0000, Rémi Bernon wrote:
This brings up the "not noticing the layout switch until the next keypress" problem though. I don't know how important it is but it might be a point in favor of listening to Xkb events when available. This seems to be a GNOME specific problem, possibly their bug, and maybe we don't have to worry about it for now. Also, I'm not sure that using Xkb events would make any difference, have you checked that it does? In fact, switching to "ru" gives me this keymap: `pc+us+ru:2+inet(evdev)`. From there switching to "it" causes another MappingNotify (or 4 actually, for some reason), reinstalling the previous keymap, while a switch to "us" would cause no MappingNotify event.
For reference, on Gnome X11 with the same layouts configured I get this keymap: `pc+us+it:2+ru:3+us:4+inet(evdev)+level3(rwin_switch)`. And indeed, switching among the "us", "it", "ru" layouts doesn't require an Xkb keymap update or generate a MappingNotify event, while flipping to a different layout will do so. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/10550#note_135707
This brings up the "not noticing the layout switch until the next keypress" problem though. I don't know how important it is but it might be a point in favor of listening to Xkb events when available.
This seems to be a GNOME specific problem, possibly their bug, and maybe we don't have to worry about it for now.
That was with KDE though. As I understand it it's more a matter of what layouts end up in the same keymap and whether you happen to switch between layouts represented by the same keymap or not. I.e., with `pc+us+ru:2+inet(evdev)` I can switch between "us" and "ru" freely without ever triggering a MappingNotify, then a switch to "it" will cause MappingNotify to be sent and the keymap to become `pc+us+it:2+inet(evdev)`. At that point I can instead flip between "it" and "us" without any MappingNotify.
Also, I'm not sure that using Xkb events would make any difference, have you checked that it does?
Yeah, XkbStateNotify is called immediately every time the layout changes but the underlying keymap doesn't (i.e. it's just an Xkb group switch), while XkbNewKeyboardNotify is triggered when the keymap does change. Both seem to work consistently on Gnome X11 and Xwayland, KDE X11. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/10550#note_135708
On Thu Apr 9 16:19:01 2026 +0000, Matteo Bruni wrote:
This brings up the "not noticing the layout switch until the next keypress" problem though. I don't know how important it is but it might be a point in favor of listening to Xkb events when available. This seems to be a GNOME specific problem, possibly their bug, and maybe we don't have to worry about it for now. That was with KDE though. As I understand it it's more a matter of what layouts end up in the same keymap and whether you happen to switch between layouts represented by the same keymap or not. I.e., with `pc+us+ru:2+inet(evdev)` I can switch between "us" and "ru" freely without ever triggering a MappingNotify, then a switch to "it" will cause MappingNotify to be sent and the keymap to become `pc+us+it:2+inet(evdev)`. At that point I can instead flip between "it" and "us" without any MappingNotify. Also, I'm not sure that using Xkb events would make any difference, have you checked that it does? Yeah, XkbStateNotify is called immediately every time the layout changes but the underlying keymap doesn't (i.e. it's just an Xkb group switch), while XkbNewKeyboardNotify is triggered when the keymap does change. Both seem to work consistently on Gnome X11 and Xwayland, KDE X11. Just to add another data point: KDE Xwayland seems to only allow switching among the first 4 defined layouts and it does so by changing the Xkb group while keeping the same keymap (thus generating a XkbStateNotify event each time). The output of setxkbmap isn't very interesting:
$ setxkbmap -print
WARNING: Running setxkbmap against an Xwayland server
xkb_keymap {
xkb_keycodes { include "evdev+aliases(qwerty)" };
xkb_types { include "complete" };
xkb_compat { include "complete" };
xkb_symbols { include "pc+us+inet(evdev)" };
xkb_geometry { include "pc(pc105)" };
};
but nonetheless, the Xkb group names have the info we need: ``` 2579.220:0020:0024:trace:keyboard:init_xkb_layouts Found 4 valid group names 2579.220:0020:0024:trace:keyboard:init_xkb_layouts Layout 0 name English (US) 2579.220:0020:0024:trace:keyboard:init_xkb_layouts Layout 1 name Italian 2579.220:0020:0024:trace:keyboard:init_xkb_layouts Layout 2 name Russian 2579.220:0020:0024:trace:keyboard:init_xkb_layouts Layout 3 name Japanese ``` -- https://gitlab.winehq.org/wine/wine/-/merge_requests/10550#note_135729
participants (3)
-
Matteo Bruni -
Matteo Bruni (@Mystral) -
Rémi Bernon (@rbernon)