[PATCH 0/5] MR10779: winex11: Keyboard layouts rework, part 2
From: Matteo Bruni <mbruni@codeweavers.com> --- dlls/win32u/input.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/dlls/win32u/input.c b/dlls/win32u/input.c index ab09c3b7d42..da203666549 100644 --- a/dlls/win32u/input.c +++ b/dlls/win32u/input.c @@ -872,7 +872,7 @@ DWORD WINAPI NtUserGetQueueStatus( UINT flags ) } /******************************************************************* - * NtUserGetThreadInfo (win32u.@) + * NtUserGetThreadState (win32u.@) */ ULONG_PTR WINAPI NtUserGetThreadState( USERTHREADSTATECLASS cls ) { @@ -977,9 +977,6 @@ static HKL get_locale_kbd_layout(void) /*********************************************************************** * NtUserGetKeyboardLayout (win32u.@) - * - * Device handle for keyboard layout defaulted to - * the language id. This is the way Windows default works. */ HKL WINAPI NtUserGetKeyboardLayout( DWORD thread_id ) { -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10779
From: Matteo Bruni <mbruni@codeweavers.com> Often the layout name matches the high word of the HKL. This is still "temporary", until we add the keyboard layouts data to the registry. --- dlls/win32u/input.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/dlls/win32u/input.c b/dlls/win32u/input.c index da203666549..8304b26e8bd 100644 --- a/dlls/win32u/input.c +++ b/dlls/win32u/input.c @@ -1454,7 +1454,17 @@ BOOL WINAPI NtUserGetKeyboardLayoutName( WCHAR *name ) layout = NtUserGetKeyboardLayout( 0 ); id = HandleToUlong( layout ); - if (HIWORD( id ) == LOWORD( id )) id = LOWORD( id ); + if ((id & 0xf0000000) == 0xf0000000) + { + static unsigned int once; + + if (!once++) + FIXME("Variant layouts are not supported yet, returning the locale.\n"); + else + WARN("Variant layouts are not supported yet, returning the locale.\n"); + id = LOWORD(id); + } + else id = HIWORD(id); snprintf( buffer, sizeof(buffer), "%08X", id ); asciiz_to_unicode( name, buffer ); -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10779
From: Matteo Bruni <mbruni@codeweavers.com> To avoid confusion with "Layout Id" which is related to layout variants. --- dlls/winex11.drv/keyboard.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/dlls/winex11.drv/keyboard.c b/dlls/winex11.drv/keyboard.c index 65de84678ec..4b7cb07feb1 100644 --- a/dlls/winex11.drv/keyboard.c +++ b/dlls/winex11.drv/keyboard.c @@ -1475,11 +1475,11 @@ static BOOL find_xkb_layout_variant( const char *name, const char **layout, cons return FALSE; } -static const struct layout_id_map_entry +static const struct langid_map_entry { const char *name; LANGID langid; -} layout_ids[] = +} langid_map[] = { { "af", MAKELANGID(LANG_DARI, SUBLANG_DEFAULT) }, { "al", MAKELANGID(LANG_ALBANIAN, SUBLANG_DEFAULT) }, @@ -1580,17 +1580,17 @@ static const struct layout_id_map_entry { "za", MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_SOUTH_AFRICA) }, }; -static int layout_id_map_cmp( const void *key, const void *element ) +static int langid_map_cmp( const void *key, const void *element ) { - const struct layout_id_map_entry *entry = element; + const struct langid_map_entry *entry = element; return strcmp( key, entry->name ); } static LANGID langid_from_xkb_layout( const char *layout ) { - struct layout_id_map_entry *entry; + struct langid_map_entry *entry; - entry = bsearch( layout, layout_ids, ARRAY_SIZE(layout_ids), sizeof(*layout_ids), layout_id_map_cmp ); + entry = bsearch( layout, langid_map, ARRAY_SIZE(langid_map), sizeof(*langid_map), langid_map_cmp ); if (entry) return entry->langid; FIXME( "Unknown layout %s\n", debugstr_a(layout) ); -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10779
From: Matteo Bruni <mbruni@codeweavers.com> --- dlls/winex11.drv/keyboard.c | 73 ++++++++++++++++++++----------------- 1 file changed, 39 insertions(+), 34 deletions(-) diff --git a/dlls/winex11.drv/keyboard.c b/dlls/winex11.drv/keyboard.c index 4b7cb07feb1..b525363039f 100644 --- a/dlls/winex11.drv/keyboard.c +++ b/dlls/winex11.drv/keyboard.c @@ -1446,9 +1446,10 @@ 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 ) { -#ifdef SONAME_LIBXKBREGISTRY struct rxkb_layout *iter; if (rxkb_context) @@ -1469,9 +1470,6 @@ static BOOL find_xkb_layout_variant( const char *name, const char **layout, cons } else WARN( "libxkbregistry not available, falling back to fuzzy layout detection\n" ); -#else - WARN( "libxkbregistry support not compiled in, falling back to fuzzy layout detection\n" ); -#endif return FALSE; } @@ -1597,6 +1595,42 @@ static LANGID langid_from_xkb_layout( const char *layout ) return MAKELANGID(LANG_NEUTRAL, SUBLANG_CUSTOM_UNSPECIFIED); }; +#endif + +static BOOL init_xkb_layouts( Display *display, XkbDescRec *xkb_desc, unsigned int xkb_group ) +{ +#ifdef SONAME_LIBXKBREGISTRY + unsigned int count; + char *names[4]; + + XkbGetNames( display, XkbGroupNamesMask, xkb_desc ); + for (count = 0; count < ARRAY_SIZE(xkb_desc->names->groups); count++) + if (!xkb_desc->names->groups[count]) break; + + if (!XGetAtomNames( display, xkb_desc->names->groups, count, names )) count = 0; + for (int i = 0; i < count; i++) + { + const char *layout, *variant = NULL; + LANGID lang; + + if (!names[i]) continue; + if (find_xkb_layout_variant( names[i], &layout, &variant )) + { + lang = langid_from_xkb_layout( layout ); + + TRACE( "Found group %u with name %s -> layout %s:%s, lang %04x\n", i, debugstr_a(names[i]), + debugstr_a(layout), debugstr_a(variant), lang ); + } + XFree( names[i] ); + } + + return TRUE; +#else + WARN( "libxkbregistry support not compiled in, falling back to fuzzy layout detection\n" ); +#endif + return FALSE; +} + /* fuzzy layout detection through keysym / keycode matching, kbd_section must be held */ static void detect_keyboard_layout( Display *display, XModifierKeymap *modmap, unsigned int xkb_group ) { @@ -1960,7 +1994,6 @@ void init_keyboard_layouts( Display *display ) XkbStateRec xkb_state; XModifierKeymap *mmp; XkbDescRec *xkb_desc; - LANGID xkb_lang = 0; Status status; KeyCode *kcp; @@ -1996,41 +2029,13 @@ void init_keyboard_layouts( Display *display ) if ((xkb_desc = XkbGetMap( display, XkbAllClientInfoMask, XkbUseCoreKbd ))) { - char *names[4]; - int count; - - XkbGetNames( display, XkbGroupNamesMask, xkb_desc ); - for (count = 0; count < ARRAY_SIZE(xkb_desc->names->groups); count++) - if (!xkb_desc->names->groups[count]) break; - - if (!XGetAtomNames( display, xkb_desc->names->groups, count, names )) count = 0; - for (int i = 0; i < count; i++) - { - const char *layout, *variant = NULL; - LANGID lang; - - if (!names[i]) continue; - if (find_xkb_layout_variant( names[i], &layout, &variant )) - { - lang = langid_from_xkb_layout( layout ); - if (i == xkb_group) xkb_lang = lang; - - TRACE( "Found group %u with name %s -> layout %s:%s, lang %04x\n", i, debugstr_a(names[i]), - debugstr_a(layout), debugstr_a(variant), lang ); - } - XFree( names[i] ); - } - + init_xkb_layouts( display, xkb_desc, xkb_group ); XkbFreeKeyboard( xkb_desc, 0, True ); } detect_keyboard_layout( display, mmp, xkb_group ); XFreeModifiermap( mmp ); - if (xkb_lang && xkb_lang != main_key_tab[kbd_layout].lcid) - WARN( "Xkb langid %04x differs from detected langid %04x\n", - xkb_lang, main_key_tab[kbd_layout].lcid ); - init_keycode_mappings( display ); pthread_mutex_unlock( &kbd_mutex ); -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10779
From: Matteo Bruni <mbruni@codeweavers.com> Very much inspired by a patch by Rémi Bernon. Similarly for some of the following patches. --- dlls/winex11.drv/keyboard.c | 53 +++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/dlls/winex11.drv/keyboard.c b/dlls/winex11.drv/keyboard.c index b525363039f..8847ce4adf5 100644 --- a/dlls/winex11.drv/keyboard.c +++ b/dlls/winex11.drv/keyboard.c @@ -55,6 +55,7 @@ #include "kbd.h" #include "wine/server.h" #include "wine/debug.h" +#include "wine/list.h" /* log format (add 0-padding as appropriate): keycode %u as in output from xev @@ -65,6 +66,14 @@ WINE_DEFAULT_DEBUG_CHANNEL(keyboard); WINE_DECLARE_DEBUG_CHANNEL(key); +struct layout +{ + struct list entry; + + int xkb_group; + char *xkb_layout; +}; + static const unsigned int ControlMask = 1 << 2; static int min_keycode, max_keycode, keysyms_per_keycode; @@ -73,9 +82,41 @@ static WORD keyc2vkey[256], keyc2scan[256]; static int NumLockMask, ScrollLockMask, AltGrMask; /* mask in the XKeyEvent state */ static pthread_mutex_t kbd_mutex = PTHREAD_MUTEX_INITIALIZER; +static struct list xkb_layouts = LIST_INIT( xkb_layouts ); static char KEYBOARD_MapDeadKeysym(KeySym keysym); +#ifdef SONAME_LIBXKBREGISTRY +static void create_layout_from_xkb( int xkb_group, const char *xkb_layout ) +{ + struct layout *layout; + + TRACE( "xkb_group %u, xkb_layout %s\n", xkb_group, xkb_layout ); + + LIST_FOR_EACH_ENTRY( layout, &xkb_layouts, struct layout, entry ) + { + if (!strcmp( layout->xkb_layout, xkb_layout )) + { + TRACE( "Found existing layout entry %p\n", layout ); + if (layout->xkb_group == -1) layout->xkb_group = xkb_group; + return; + } + } + + if (!(layout = calloc( 1, sizeof(*layout) + strlen( xkb_layout ) + 1 ))) + { + WARN( "Failed to allocate memory for Xkb layout entry\n" ); + return; + } + list_add_tail( &xkb_layouts, &layout->entry ); + + layout->xkb_group = xkb_group; + layout->xkb_layout = strcpy( (char *)(layout + 1), xkb_layout ); + + TRACE( "Created layout entry %p\n", layout ); +} +#endif + /* Keyboard translation tables */ #define MAIN_LEN 49 static const WORD main_key_scan_qwerty[MAIN_LEN] = @@ -1599,18 +1640,27 @@ static LANGID langid_from_xkb_layout( const char *layout ) static BOOL init_xkb_layouts( Display *display, XkbDescRec *xkb_desc, unsigned int xkb_group ) { + struct layout *entry; #ifdef SONAME_LIBXKBREGISTRY unsigned int count; char *names[4]; +#endif + /* Flag any previously created Xkb layout as invalid */ + LIST_FOR_EACH_ENTRY( entry, &xkb_layouts, struct layout, entry ) + entry->xkb_group = -1; + +#ifdef SONAME_LIBXKBREGISTRY XkbGetNames( display, XkbGroupNamesMask, xkb_desc ); for (count = 0; count < ARRAY_SIZE(xkb_desc->names->groups); count++) if (!xkb_desc->names->groups[count]) break; if (!XGetAtomNames( display, xkb_desc->names->groups, count, names )) count = 0; + TRACE("Found %u group names\n", count); for (int i = 0; i < count; i++) { const char *layout, *variant = NULL; + char buffer[1024]; LANGID lang; if (!names[i]) continue; @@ -1620,6 +1670,9 @@ static BOOL init_xkb_layouts( Display *display, XkbDescRec *xkb_desc, unsigned i TRACE( "Found group %u with name %s -> layout %s:%s, lang %04x\n", i, debugstr_a(names[i]), debugstr_a(layout), debugstr_a(variant), lang ); + + snprintf( buffer, ARRAY_SIZE(buffer), "%s:%s", layout, variant ); + create_layout_from_xkb( i, buffer ); } XFree( names[i] ); } -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10779
Rémi Bernon (@rbernon) commented about dlls/winex11.drv/keyboard.c:
return MAKELANGID(LANG_NEUTRAL, SUBLANG_CUSTOM_UNSPECIFIED); };
+#endif + +static BOOL init_xkb_layouts( Display *display, XkbDescRec *xkb_desc, unsigned int xkb_group )
Please lets avoid moving and renaming things that were just introduced in the previous batch. Also this only adds more code under conditional compilation and I think we should try moving the other way. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/10779#note_138308
Rémi Bernon (@rbernon) commented about dlls/winex11.drv/keyboard.c:
static int NumLockMask, ScrollLockMask, AltGrMask; /* mask in the XKeyEvent state */
static pthread_mutex_t kbd_mutex = PTHREAD_MUTEX_INITIALIZER; +static struct list xkb_layouts = LIST_INIT( xkb_layouts );
static char KEYBOARD_MapDeadKeysym(KeySym keysym);
+#ifdef SONAME_LIBXKBREGISTRY +static void create_layout_from_xkb( int xkb_group, const char *xkb_layout ) Here as well, lets not make this conditionally compiled unless it actually does libxkbregistry calls. Especially as I understand you are planning on doing that with the old detection code too which means you would then have to move or remove conditionally compiled code again.
-- https://gitlab.winehq.org/wine/wine/-/merge_requests/10779#note_138309
Rémi Bernon (@rbernon) commented about dlls/winex11.drv/keyboard.c:
return FALSE; }
-static const struct layout_id_map_entry +static const struct langid_map_entry { const char *name; LANGID langid; -} layout_ids[] = +} langid_map[] =
I don't really mind but we just introduced this, lets stick with whatever name it was given and move forward with meaningful changes. It can be renamed later, for instance whenever it gets factored with winewayland one way or another. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/10779#note_138310
Rémi Bernon (@rbernon) commented about dlls/win32u/input.c:
layout = NtUserGetKeyboardLayout( 0 ); id = HandleToUlong( layout ); - if (HIWORD( id ) == LOWORD( id )) id = LOWORD( id ); + if ((id & 0xf0000000) == 0xf0000000) + { + static unsigned int once; + + if (!once++) + FIXME("Variant layouts are not supported yet, returning the locale.\n"); + else + WARN("Variant layouts are not supported yet, returning the locale.\n"); + id = LOWORD(id); + } + else id = HIWORD(id);
Does this fix or help anything? Sure we can make layout ids more compatible but if it doesn't matter so much (and as far as I know it doesn't), we should work on the meaningful bits first. Especially if this is only temporary. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/10779#note_138311
On Thu Apr 30 07:33:56 2026 +0000, Rémi Bernon wrote:
Please lets avoid moving and renaming things that were just introduced in the previous batch. Also this only adds more code under conditional compilation and I think we should try moving the other way. It does get rid of an indentation level though! More importantly, it does simplify the fallback a bit.
-- https://gitlab.winehq.org/wine/wine/-/merge_requests/10779#note_138338
On Thu Apr 30 07:33:56 2026 +0000, Rémi Bernon wrote:
Here as well, lets not make this conditionally compiled unless it actually does libxkbregistry calls. Especially as I understand you are planning on doing that with the old detection code too which means you would then have to move or remove conditionally compiled code again. Only the #ifdef is going to move around. Making this unconditional will generate warnings when libxkbregistry is not available at compile time.
-- https://gitlab.winehq.org/wine/wine/-/merge_requests/10779#note_138339
On Thu Apr 30 07:33:56 2026 +0000, Rémi Bernon wrote:
I don't really mind but we just introduced this, lets stick with whatever name it was given and move forward with meaningful changes. It can be renamed later, for instance whenever it gets factored with winewayland one way or another. I can drop this patch, it was more important in an earlier version when the name clash was actually in the way. It's still a bit confusing though.
-- https://gitlab.winehq.org/wine/wine/-/merge_requests/10779#note_138340
On Thu Apr 30 09:14:27 2026 +0000, Rémi Bernon wrote:
Does this fix or help anything? Sure we can make layout ids more compatible but if it doesn't matter so much (and as far as I know it doesn't), we should work on the meaningful bits first. Especially if this is only temporary. It's temporary until we add the full "Keyboard Layouts" DB to the registry and make the (already existing) real implementation of `NtUserGetKeyboardLayoutName()`, a few lines below, work. Otherwise, yes, the bug I'm attempting to fix with this rework very much depends on getting this right (or right enough). We want to use the detected keyboard layout LANGID for the layout name (i.e. usually the high WORD of the HKL), specifically when the locale doesn't match with the keyboard layout. The game uses the layout name alone to show proper key labels in the interface.
Using the low WORD of the HKL to store the LANGID of the keyboard layout, at least in the case of layout variants (0xfxxxxxxx HKL), is another option, although that requires tweaking the "changing user locale" FIXME() and early exit in `NtUserActivateKeyboardLayout()` accordingly, and it's arguably a (temporary) step in the opposite direction to where we want to end up. Otherwise I could push the addition of the registry data (and updating all the drivers to make use of that, or at least not break in the new world) towards the front of the patch sequence, moving the fix I want to get to a bit further, so that this becomes "temporary" enough that I can drop this change. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/10779#note_138345
On Thu Apr 30 09:46:12 2026 +0000, Matteo Bruni wrote:
It's temporary until we add the full "Keyboard Layouts" DB to the registry and make the (already existing) real implementation of `NtUserGetKeyboardLayoutName()`, a few lines below, work. Otherwise, yes, the bug I'm attempting to fix with this rework very much depends on getting this right (or right enough). We want to use the detected keyboard layout LANGID for the layout name (i.e. usually the high WORD of the HKL), specifically when the locale doesn't match with the keyboard layout. The game uses the layout name alone to show proper key labels in the interface. Using the low WORD of the HKL to store the LANGID of the keyboard layout, at least in the case of layout variants (0xfxxxxxxx HKL), is another option, although that requires tweaking the "changing user locale" FIXME() and early exit in `NtUserActivateKeyboardLayout()` accordingly, and it's arguably a (temporary) step in the opposite direction to where we want to end up. Otherwise I could push the addition of the registry data (and updating all the drivers to make use of that, or at least not break in the new world) towards the front of the patch sequence, moving the fix I want to get to a bit further, so that this becomes "temporary" enough that I can drop this change. It seems to me that fixing layout ids is orthogonal to fixing the layout detection in winex11, and that it can be done in win32u only. If this is what you want to do first, and if the layout ids are indeed stable on Windows (which a couple of tests could help assert), then I think we can do that but it's probably just about adding some predefined "Layout Id" registry entries for well known layouts.
Later maybe it should live in some keyboard layout DLLs, but right now I think as the definition is mostly in win32u and user drivers that could probably go in wine.inf, or some win32u reg script. Whether we want to have all of them or only the most common ones is also a question (according to https://kbdlayout.info, native has 217 builtin layouts). -- https://gitlab.winehq.org/wine/wine/-/merge_requests/10779#note_138352
On Thu Apr 30 10:01:48 2026 +0000, Rémi Bernon wrote:
It seems to me that fixing layout ids is orthogonal to fixing the layout detection in winex11, and that it can be done in win32u only. If this is what you want to do first, and if the layout ids are indeed stable on Windows (which a couple of tests could help assert), then I think we can do that but it's probably just about adding some predefined "Layout Id" registry entries for well known layouts. Later maybe it should live in some keyboard layout DLLs, but right now I think as the definition is mostly in win32u and user drivers that could probably go in wine.inf, or some win32u reg script. Whether we want to have all of them or only the most common ones is also a question (according to https://kbdlayout.info, native has 217 builtin layouts). Sorry maybe I'm wrong but I just don't see where this is going with the few patches here. If you have a larger series with the bigger picture please push it somewhere so I can make up my mind.
-- https://gitlab.winehq.org/wine/wine/-/merge_requests/10779#note_138355
On Thu Apr 30 10:17:05 2026 +0000, Rémi Bernon wrote:
Sorry maybe I'm wrong but I just don't see where this is going with the few patches here. If you have a larger series with the bigger picture please push it somewhere so I can make up my mind. It's not entirely orthogonal because of those interactions with `ActivateKeyboardLayout` and `GetKeyboardLayoutName`, especially the latter has to figure out the keyboard layout from the HKL alone if there is no registry data to help, and something like this patch is needed. Although, one thing I should mention is that "Layout Id" is only present in the registry for variant layouts[*], so this kind of change will need to stay around even after the registry data is in place. I'll tweak the patch to make that more clear.
Yes, the idea was to put all the data in wine.inf. And yes, the full DB is huge, but I guess it can be brought in in steps... [*]: e.g. KLID 00000409 (US) has no "Layout Id" entry and a HKL 0x0409<locale id>, KLID 00010409 (United States-Dvorak) has "Layout Id" 0002 and thus HKL 0xf002<locale id> -- https://gitlab.winehq.org/wine/wine/-/merge_requests/10779#note_138356
On Thu Apr 30 10:30:23 2026 +0000, Matteo Bruni wrote:
It's not entirely orthogonal because of those interactions with `ActivateKeyboardLayout` and `GetKeyboardLayoutName`, especially the latter has to figure out the keyboard layout from the HKL alone if there is no registry data to help, and something like this patch is needed. Although, one thing I should mention is that "Layout Id" is only present in the registry for variant layouts[*], so this kind of change will need to stay around even after the registry data is in place. I'll tweak the patch to make that more clear. Yes, the idea was to put all the data in wine.inf. And yes, the full DB is huge, but I guess it can be brought in in steps... [*]: e.g. KLID 00000409 (US) has no "Layout Id" entry and HKL 0x0409{locale id}, KLID 00010409 (United States-Dvorak) has "Layout Id" 0002 and thus HKL 0xf002{locale id} Yes, when "Layout Id" isn't set a registry key is meant for the HKL which high word matches the registry key name. When "Layout Id" is set, the registry key is meant for the HKL which high word matches the ("Layout Id" | 0xf000). Isn't this what `NtUserGetKeyboardLayoutName` does already? The KLID, returned by `GetKeyboardLayoutName` is the name of the registry key itself, which is the same as the HKL high word for base layouts, or something different for variants.
It seems to me that the line that changed here has nothing to do with this I believe and is only meant as a workaround for when registry entries are not present? We should simply add the proper registry keys if they are necessary. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/10779#note_138357
The KLID, returned by `GetKeyboardLayoutName` is the name of the registry key itself, which is the same as the HKL high word for base layouts, or something different for variants.
Exactly. The idea of this change is to short-circuit the base layout case, avoiding the pointless registry access entirely. It doesn't do that though, while it probably should (the next revision will). -- https://gitlab.winehq.org/wine/wine/-/merge_requests/10779#note_138379
On Thu Apr 30 13:40:55 2026 +0000, Matteo Bruni wrote:
The KLID, returned by `GetKeyboardLayoutName` is the name of the registry key itself, which is the same as the HKL high word for base layouts, or something different for variants. Exactly. The idea of this change is to short-circuit the base layout case, avoiding the pointless registry access entirely. It doesn't do that though, while it probably should (the next revision will). It turns out this is already covered by the tests: https://gitlab.winehq.org/wine/wine/-/blob/1d744d4ca948c7ea403a786f21c5fe4ef...
-- https://gitlab.winehq.org/wine/wine/-/merge_requests/10779#note_138408
participants (3)
-
Matteo Bruni -
Matteo Bruni (@Mystral) -
Rémi Bernon (@rbernon)