[PATCH v2 0/6] 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... -- v2: winex11: Decode current layout from Xkb group name. winex11: Update an obsolete comment about the Xkb group index. 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: Factor out Xkb initialization. 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 | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/dlls/winex11.drv/keyboard.c b/dlls/winex11.drv/keyboard.c index e2281bef017..4a9b8d5e0c6 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,31 @@ 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] ); + } + XkbFreeNames( xkb_desc, XkbGroupNamesMask, True ); + XkbFreeClientMap( xkb_desc, 0, True ); + 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> --- 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 4a9b8d5e0c6..15f4cf8865f 100644 --- a/dlls/winex11.drv/keyboard.c +++ b/dlls/winex11.drv/keyboard.c @@ -1883,6 +1883,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 8b0c0a62f91..13d8260032e 100644 --- a/dlls/winex11.drv/x11drv.h +++ b/dlls/winex11.drv/x11drv.h @@ -755,6 +755,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> 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 | 286 +++++++++++++++++++----------------- 1 file changed, 154 insertions(+), 132 deletions(-) diff --git a/dlls/winex11.drv/keyboard.c b/dlls/winex11.drv/keyboard.c index 15f4cf8865f..9c18dc9f96a 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,64 +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] ); - } - XkbFreeNames( xkb_desc, XkbGroupNamesMask, True ); - XkbFreeClientMap( xkb_desc, 0, True ); - 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; @@ -1670,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; @@ -1736,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!) */ @@ -1744,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); } } @@ -1787,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]) { @@ -1809,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) { @@ -1832,17 +1773,98 @@ 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] ); + } + XkbFreeNames( xkb_desc, XkbGroupNamesMask, True ); + XkbFreeClientMap( xkb_desc, 0, True ); + XkbFreeKeyboard( xkb_desc, XkbNamesMask, 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 | 31 +++++++++++++++++++++++-------- 1 file changed, 23 insertions(+), 8 deletions(-) diff --git a/dlls/winex11.drv/keyboard.c b/dlls/winex11.drv/keyboard.c index 9c18dc9f96a..f52a94f149d 100644 --- a/dlls/winex11.drv/keyboard.c +++ b/dlls/winex11.drv/keyboard.c @@ -1443,15 +1443,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 +1473,13 @@ 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++) + { + 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 +1850,6 @@ void X11DRV_InitKeyboard( Display *display ) } } } - XFreeModifiermap(mmp); s = XkbGetState( display, XkbUseCoreKbd, &xkb_state ); xkb_group = s ? 0 : xkb_state.group; @@ -1861,8 +1876,8 @@ void X11DRV_InitKeyboard( Display *display ) XkbFreeKeyboard( xkb_desc, XkbNamesMask, 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> --- dlls/winex11.drv/keyboard.c | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/dlls/winex11.drv/keyboard.c b/dlls/winex11.drv/keyboard.c index f52a94f149d..59cdba175d2 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); @@ -1478,6 +1469,10 @@ X11DRV_KEYBOARD_DetectLayout( Display *display, XModifierKeymap *modmap, unsigne /* get data for keycode from X server */ 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 */ -- 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 | 121 ++++++++++++++++++++-- configure.ac | 10 +- dlls/winex11.drv/Makefile.in | 4 +- dlls/winex11.drv/keyboard.c | 189 ++++++++++++++++++++++++++++++++++- include/config.h.in | 3 + 5 files changed, 307 insertions(+), 20 deletions(-) diff --git a/configure b/configure index a70cf372fec..ad81d29f591 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 @@ -16720,6 +16720,102 @@ 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 -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 + + opengl_msg="" if test "x$with_opengl" != "xno" then @@ -17093,7 +17189,9 @@ fi CPPFLAGS=$ac_save_CPPFLAGS fi - rm -f conftest.err + if test "x$have_x" != "xyes" + then + rm -f conftest.err if ${XKBREGISTRY_CFLAGS:+false} : then : if test ${PKG_CONFIG+y} @@ -17176,6 +17274,7 @@ fi CPPFLAGS=$ac_save_CPPFLAGS fi + fi if test "x$with_opengl" != "xno" then rm -f conftest.err @@ -25174,13 +25273,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 9ee0e765a15..8adbad625d5 100644 --- a/configure.ac +++ b/configure.ac @@ -1406,6 +1406,9 @@ then dnl *** End of X11/Xlib.h check + WINE_PACKAGE_FLAGS(XKBREGISTRY,[xkbregistry],,,, + [WINE_CHECK_SONAME(xkbregistry,rxkb_context_new,[:],[XKBREGISTRY_LIBS=""],[$XKBREGISTRY_LIBS])]) + dnl Check for the presence of OpenGL opengl_msg="" if test "x$with_opengl" != "xno" @@ -1443,8 +1446,11 @@ 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$have_x" != "xyes" + then + WINE_PACKAGE_FLAGS(XKBREGISTRY,[xkbregistry],,,, + [AC_CHECK_LIB(xkbregistry,rxkb_context_new,[:],[XKBREGISTRY_LIBS=""],[$XKBREGISTRY_LIBS])]) + fi 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 59cdba175d2..e8358b855e7 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,154 @@ 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; +} +#endif + +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) }, +}; + +#ifdef SONAME_LIBXKBREGISTRY +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 +1963,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 +2012,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,11 +2023,17 @@ 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])); - XFree( xkb_group_names[i] ); - } XkbFreeNames( xkb_desc, XkbGroupNamesMask, True ); + + 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 XkbFreeClientMap( xkb_desc, 0, True ); XkbFreeKeyboard( xkb_desc, XkbNamesMask, True ); } @@ -1874,6 +2041,9 @@ void X11DRV_InitKeyboard( Display *display ) 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 ); @@ -1918,6 +2088,15 @@ BOOL X11DRV_MappingNotify( HWND dummy, XEvent *event ) 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 } void x11drv_xkb_init_thread( struct x11drv_thread_data *data ) 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
Matteo Bruni (@Mystral) commented about dlls/winex11.drv/keyboard.c:
+ *variant = rxkb_layout_get_variant(iter); + return TRUE; + } + } + + return FALSE; +} +#endif + +static const struct layout_id_map_entry +{ + const char *name; + LANGID langid; +} layout_ids[] = +{ + { "af", MAKELANGID(LANG_DARI, SUBLANG_DEFAULT) }, Maybe worth mentioning that I turned this into a "normal" sorted array (compared to winewayland's version) because, later on, I'm going to reuse it in reverse for creating `struct layout` layouts from `X11DRV_KEYBOARD_DetectLayout()` in the fallback case.
-- https://gitlab.winehq.org/wine/wine/-/merge_requests/10550#note_136352
Rémi Bernon (@rbernon) commented about dlls/winex11.drv/keyboard.c:
+ { "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) }, +}; + +#ifdef SONAME_LIBXKBREGISTRY The ifdef could be extended from the part above layout_ids.
-- https://gitlab.winehq.org/wine/wine/-/merge_requests/10550#note_136450
Rémi Bernon (@rbernon) commented about configure.ac:
[WAYLAND_CLIENT_LIBS=""],[$WAYLAND_CLIENT_LIBS])])]) WINE_PACKAGE_FLAGS(XKBCOMMON,[xkbcommon],,,, [AC_CHECK_LIB(xkbcommon,xkb_context_new,[:],[XKBCOMMON_LIBS=""],[$XKBCOMMON_LIBS])]) + if test "x$have_x" != "xyes" + then WINE_PACKAGE_FLAGS(XKBREGISTRY,[xkbregistry],,,, [AC_CHECK_LIB(xkbregistry,rxkb_context_new,[:],[XKBREGISTRY_LIBS=""],[$XKBREGISTRY_LIBS])]) + fi
What about moving this together somewhere else, with a test that includes both Wayland and X at once instead of duplicating the package check? -- https://gitlab.winehq.org/wine/wine/-/merge_requests/10550#note_136451
Rémi Bernon (@rbernon) commented about dlls/winex11.drv/keyboard.c:
+ 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] ); + } + XkbFreeNames( xkb_desc, XkbGroupNamesMask, True ); + XkbFreeClientMap( xkb_desc, 0, True ); + XkbFreeKeyboard( xkb_desc, XkbNamesMask, True ); IIUC from XkbFreeKeyboard documentation, with free_all = True everything gets freed, so there's no need to have separate calls.
-- https://gitlab.winehq.org/wine/wine/-/merge_requests/10550#note_136452
Rémi Bernon (@rbernon) commented about dlls/winex11.drv/keyboard.c:
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 ); +}
It doesn't seem very useful, let's delay this until and if we actually have something to add to these. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/10550#note_136453
Rémi Bernon (@rbernon) commented about dlls/winex11.drv/keyboard.c:
/* get data for keycode from X server */ 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);
This could/should probably be squashed with previous change. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/10550#note_136454
On Wed Apr 15 19:46:10 2026 +0000, Matteo Bruni wrote:
Maybe worth mentioning that I turned this into a "normal" sorted array (compared to winewayland's version) because, later on, I'm going to reuse it in reverse for creating `struct layout` layouts from `X11DRV_KEYBOARD_DetectLayout()` in the fallback case. 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).
-- https://gitlab.winehq.org/wine/wine/-/merge_requests/10550#note_136455
participants (3)
-
Matteo Bruni -
Matteo Bruni (@Mystral) -
Rémi Bernon (@rbernon)