From d64ea8e4a6c93ba77208cb680b91dc0c2830049d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 21 Nov 2023 12:52:10 +0100 Subject: [PATCH] winewayland.drv: Enumerate Xkb layouts and create matching HKL. --- configure | 90 +++++++- configure.ac | 5 +- dlls/winewayland.drv/Makefile.in | 4 +- dlls/winewayland.drv/wayland_keyboard.c | 259 +++++++++++++++++++++++- dlls/winewayland.drv/waylanddrv.h | 1 + include/config.h.in | 3 + 6 files changed, 356 insertions(+), 6 deletions(-) diff --git a/configure b/configure index 3b8ec628bfb..b54e50cd7a9 100755 --- a/configure +++ b/configure @@ -702,6 +702,8 @@ INOTIFY_LIBS INOTIFY_CFLAGS PCSCLITE_LIBS PCAP_LIBS +XKBREGISTRY_LIBS +XKBREGISTRY_CFLAGS XKBCOMMON_LIBS XKBCOMMON_CFLAGS WAYLAND_SCANNER @@ -1797,6 +1799,8 @@ WAYLAND_CLIENT_CFLAGS WAYLAND_CLIENT_LIBS XKBCOMMON_CFLAGS XKBCOMMON_LIBS +XKBREGISTRY_CFLAGS +XKBREGISTRY_LIBS INOTIFY_CFLAGS INOTIFY_LIBS DBUS_CFLAGS @@ -2620,6 +2624,10 @@ 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 INOTIFY_CFLAGS C compiler flags for libinotify, overriding pkg-config INOTIFY_LIBS @@ -15956,8 +15964,86 @@ fi CPPFLAGS=$ac_save_CPPFLAGS + 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 -if test -z "$WAYLAND_CLIENT_LIBS" -o -z "$WAYLAND_SCANNER" -o -z "$XKBCOMMON_LIBS" -o "$ac_cv_header_linux_input_h" = "no" +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 +ac_save_CPPFLAGS=$CPPFLAGS +CPPFLAGS="$CPPFLAGS $XKBREGISTRY_CFLAGS" +ac_fn_c_check_header_compile "$LINENO" "xkbcommon/xkbregistry.h" "ac_cv_header_xkbcommon_xkbregistry_h" "$ac_includes_default" +if test "x$ac_cv_header_xkbcommon_xkbregistry_h" = xyes +then : + printf "%s\n" "#define HAVE_XKBCOMMON_XKBREGISTRY_H 1" >>confdefs.h + +fi + + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for rxkb_context_new in -lxkbregistry" >&5 +printf %s "checking for rxkb_context_new in -lxkbregistry... " >&6; } +if test ${ac_cv_lib_xkbregistry_rxkb_context_new+y} +then : + printf %s "(cached) " >&6 +else $as_nop + ac_check_lib_save_LIBS=$LIBS +LIBS="-lxkbregistry $XKBREGISTRY_LIBS $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +char rxkb_context_new (); +int +main (void) +{ +return rxkb_context_new (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO" +then : + ac_cv_lib_xkbregistry_rxkb_context_new=yes +else $as_nop + ac_cv_lib_xkbregistry_rxkb_context_new=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.beam \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_xkbregistry_rxkb_context_new" >&5 +printf "%s\n" "$ac_cv_lib_xkbregistry_rxkb_context_new" >&6; } +if test "x$ac_cv_lib_xkbregistry_rxkb_context_new" = xyes +then : + : +else $as_nop + XKBREGISTRY_LIBS="" +fi + +CPPFLAGS=$ac_save_CPPFLAGS + +fi +if test -z "$WAYLAND_CLIENT_LIBS" -o -z "$WAYLAND_SCANNER" -o -z "$XKBCOMMON_LIBS" -o -z "$XKBREGISTRY_LIBS" -o "$ac_cv_header_linux_input_h" = "no" then : case "x$with_wayland" in x) as_fn_append wine_notices "|Wayland ${notice_platform}development files not found, the Wayland driver won't be supported." ;; @@ -23545,6 +23631,8 @@ 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 PCAP_LIBS = $PCAP_LIBS PCSCLITE_LIBS = $PCSCLITE_LIBS INOTIFY_CFLAGS = $INOTIFY_CFLAGS diff --git a/configure.ac b/configure.ac index 074086c586b..cafe3dbc047 100644 --- a/configure.ac +++ b/configure.ac @@ -1380,8 +1380,11 @@ then WINE_PACKAGE_FLAGS(XKBCOMMON,[xkbcommon],,,, [AC_CHECK_HEADERS([xkbcommon/xkbcommon.h]) AC_CHECK_LIB(xkbcommon,xkb_context_new,[:],[XKBCOMMON_LIBS=""],[$XKBCOMMON_LIBS])]) + WINE_PACKAGE_FLAGS(XKBREGISTRY,[xkbregistry],,,, + [AC_CHECK_HEADERS([xkbcommon/xkbregistry.h]) + AC_CHECK_LIB(xkbregistry,rxkb_context_new,[:],[XKBREGISTRY_LIBS=""],[$XKBREGISTRY_LIBS])]) fi -WINE_NOTICE_WITH(wayland, [test -z "$WAYLAND_CLIENT_LIBS" -o -z "$WAYLAND_SCANNER" -o -z "$XKBCOMMON_LIBS" -o "$ac_cv_header_linux_input_h" = "no"], +WINE_NOTICE_WITH(wayland, [test -z "$WAYLAND_CLIENT_LIBS" -o -z "$WAYLAND_SCANNER" -o -z "$XKBCOMMON_LIBS" -o -z "$XKBREGISTRY_LIBS" -o "$ac_cv_header_linux_input_h" = "no"], [Wayland ${notice_platform}development files not found, the Wayland driver won't be supported.], [enable_winewayland_drv]) diff --git a/dlls/winewayland.drv/Makefile.in b/dlls/winewayland.drv/Makefile.in index fa2beaad79c..8be78bd2080 100644 --- a/dlls/winewayland.drv/Makefile.in +++ b/dlls/winewayland.drv/Makefile.in @@ -1,7 +1,7 @@ MODULE = winewayland.drv UNIXLIB = winewayland.so -UNIX_CFLAGS = $(WAYLAND_CLIENT_CFLAGS) $(XKBCOMMON_CFLAGS) -UNIX_LIBS = -lwin32u $(WAYLAND_CLIENT_LIBS) $(XKBCOMMON_LIBS) $(PTHREAD_LIBS) -lm +UNIX_CFLAGS = $(WAYLAND_CLIENT_CFLAGS) $(XKBCOMMON_CFLAGS) $(XKBREGISTRY_CFLAGS) +UNIX_LIBS = -lwin32u $(WAYLAND_CLIENT_LIBS) $(XKBCOMMON_LIBS) $(XKBREGISTRY_LIBS) $(PTHREAD_LIBS) -lm SOURCES = \ display.c \ diff --git a/dlls/winewayland.drv/wayland_keyboard.c b/dlls/winewayland.drv/wayland_keyboard.c index ab6aa3f1ac7..847810e9bb2 100644 --- a/dlls/winewayland.drv/wayland_keyboard.c +++ b/dlls/winewayland.drv/wayland_keyboard.c @@ -24,6 +24,7 @@ #endif #include "config.h" +#include #include #undef SW_MAX /* Also defined in winuser.rh */ @@ -36,6 +37,23 @@ WINE_DEFAULT_DEBUG_CHANNEL(keyboard); WINE_DECLARE_DEBUG_CHANNEL(key); +struct layout +{ + struct list entry; + char *xkb_layout; + + int xkb_group; + LANGID lang; + WORD index; + /* "Layout Id", used by NtUserGetKeyboardLayoutName / LoadKeyboardLayoutW */ + WORD layout_id; +}; + +/* These are only used from the wayland event thread and don't need locking */ +static struct list xkb_layouts = LIST_INIT(xkb_layouts); +static struct rxkb_context *rxkb_context; +static HKL keyboard_hkl; /* the HKL matching the currently active xkb group */ + static WORD key2scan(UINT key) { /* base keys can be mapped directly */ @@ -122,6 +140,198 @@ static WORD key2scan(UINT key) return 0x200 | (key & 0x7f); } +static inline LANGID langid_from_xkb_layout(const char *layout, size_t layout_len) +{ +#define MAKEINDEX(c0, c1) (MAKEWORD(c0, c1) - MAKEWORD('a', 'a')) + static const LANGID langids[] = + { + [MAKEINDEX('a','f')] = MAKELANGID(LANG_DARI, SUBLANG_DEFAULT), + [MAKEINDEX('a','l')] = MAKELANGID(LANG_ALBANIAN, SUBLANG_DEFAULT), + [MAKEINDEX('a','m')] = MAKELANGID(LANG_ARMENIAN, SUBLANG_DEFAULT), + [MAKEINDEX('a','t')] = MAKELANGID(LANG_GERMAN, SUBLANG_GERMAN_AUSTRIAN), + [MAKEINDEX('a','z')] = MAKELANGID(LANG_AZERBAIJANI, SUBLANG_DEFAULT), + [MAKEINDEX('a','u')] = MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_AUS), + [MAKEINDEX('b','a')] = MAKELANGID(LANG_BOSNIAN, SUBLANG_BOSNIAN_BOSNIA_HERZEGOVINA_CYRILLIC), + [MAKEINDEX('b','d')] = MAKELANGID(LANG_BANGLA, SUBLANG_DEFAULT), + [MAKEINDEX('b','e')] = MAKELANGID(LANG_FRENCH, SUBLANG_FRENCH_BELGIAN), + [MAKEINDEX('b','g')] = MAKELANGID(LANG_BULGARIAN, SUBLANG_DEFAULT), + [MAKEINDEX('b','r')] = MAKELANGID(LANG_PORTUGUESE, 2), + [MAKEINDEX('b','t')] = MAKELANGID(LANG_TIBETAN, 3), + [MAKEINDEX('b','w')] = MAKELANGID(LANG_TSWANA, SUBLANG_TSWANA_BOTSWANA), + [MAKEINDEX('b','y')] = MAKELANGID(LANG_BELARUSIAN, SUBLANG_DEFAULT), + [MAKEINDEX('c','a')] = MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_CAN), + [MAKEINDEX('c','d')] = MAKELANGID(LANG_FRENCH, SUBLANG_CUSTOM_UNSPECIFIED), + [MAKEINDEX('c','h')] = MAKELANGID(LANG_GERMAN, SUBLANG_GERMAN_SWISS), + [MAKEINDEX('c','m')] = MAKELANGID(LANG_FRENCH, 11), + [MAKEINDEX('c','n')] = MAKELANGID(LANG_CHINESE, SUBLANG_DEFAULT), + [MAKEINDEX('c','z')] = MAKELANGID(LANG_CZECH, SUBLANG_DEFAULT), + [MAKEINDEX('d','e')] = MAKELANGID(LANG_GERMAN, SUBLANG_DEFAULT), + [MAKEINDEX('d','k')] = MAKELANGID(LANG_DANISH, SUBLANG_DEFAULT), + [MAKEINDEX('d','z')] = MAKELANGID(LANG_TAMAZIGHT, SUBLANG_TAMAZIGHT_ALGERIA_LATIN), + [MAKEINDEX('e','e')] = MAKELANGID(LANG_ESTONIAN, SUBLANG_DEFAULT), + [MAKEINDEX('e','s')] = MAKELANGID(LANG_SPANISH, SUBLANG_DEFAULT), + [MAKEINDEX('e','t')] = MAKELANGID(LANG_AMHARIC, SUBLANG_DEFAULT), + [MAKEINDEX('f','i')] = MAKELANGID(LANG_FINNISH, SUBLANG_DEFAULT), + [MAKEINDEX('f','o')] = MAKELANGID(LANG_FAEROESE, SUBLANG_DEFAULT), + [MAKEINDEX('f','r')] = MAKELANGID(LANG_FRENCH, SUBLANG_DEFAULT), + [MAKEINDEX('g','b')] = MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_UK), + [MAKEINDEX('g','e')] = MAKELANGID(LANG_GEORGIAN, SUBLANG_DEFAULT), + [MAKEINDEX('g','h')] = MAKELANGID(LANG_ENGLISH, SUBLANG_CUSTOM_UNSPECIFIED), + [MAKEINDEX('g','n')] = MAKELANGID(LANG_NEUTRAL, SUBLANG_CUSTOM_DEFAULT), + [MAKEINDEX('g','r')] = MAKELANGID(LANG_GREEK, SUBLANG_DEFAULT), + [MAKEINDEX('h','r')] = MAKELANGID(LANG_CROATIAN, SUBLANG_DEFAULT), + [MAKEINDEX('h','u')] = MAKELANGID(LANG_HUNGARIAN, SUBLANG_DEFAULT), + [MAKEINDEX('i','d')] = MAKELANGID(LANG_INDONESIAN, SUBLANG_DEFAULT), + [MAKEINDEX('i','e')] = MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_EIRE), + [MAKEINDEX('i','l')] = MAKELANGID(LANG_HEBREW, SUBLANG_DEFAULT), + [MAKEINDEX('i','n')] = MAKELANGID(LANG_HINDI, SUBLANG_DEFAULT), + [MAKEINDEX('i','q')] = MAKELANGID(LANG_ARABIC, SUBLANG_ARABIC_IRAQ), + [MAKEINDEX('i','r')] = MAKELANGID(LANG_PERSIAN, SUBLANG_DEFAULT), + [MAKEINDEX('i','s')] = MAKELANGID(LANG_ICELANDIC, SUBLANG_DEFAULT), + [MAKEINDEX('i','t')] = MAKELANGID(LANG_ITALIAN, SUBLANG_DEFAULT), + [MAKEINDEX('j','p')] = MAKELANGID(LANG_JAPANESE, SUBLANG_DEFAULT), + [MAKEINDEX('k','e')] = MAKELANGID(LANG_NEUTRAL, SUBLANG_CUSTOM_DEFAULT), + [MAKEINDEX('k','g')] = MAKELANGID(LANG_KYRGYZ, SUBLANG_DEFAULT), + [MAKEINDEX('k','h')] = MAKELANGID(LANG_KHMER, SUBLANG_DEFAULT), + [MAKEINDEX('k','r')] = MAKELANGID(LANG_KOREAN, SUBLANG_DEFAULT), + [MAKEINDEX('k','z')] = MAKELANGID(LANG_KAZAK, SUBLANG_DEFAULT), + [MAKEINDEX('l','a')] = MAKELANGID(LANG_LAO, SUBLANG_DEFAULT), + [MAKEINDEX('l','k')] = MAKELANGID(LANG_SINHALESE, SUBLANG_DEFAULT), + [MAKEINDEX('l','t')] = MAKELANGID(LANG_LITHUANIAN, SUBLANG_DEFAULT), + [MAKEINDEX('l','v')] = MAKELANGID(LANG_LATVIAN, SUBLANG_DEFAULT), + [MAKEINDEX('m','a')] = MAKELANGID(LANG_ARABIC, SUBLANG_ARABIC_MOROCCO), + [MAKEINDEX('m','d')] = MAKELANGID(LANG_ROMANIAN, SUBLANG_CUSTOM_UNSPECIFIED), + [MAKEINDEX('m','e')] = MAKELANGID(LANG_SERBIAN, SUBLANG_SERBIAN_MONTENEGRO_LATIN), + [MAKEINDEX('m','k')] = MAKELANGID(LANG_MACEDONIAN, SUBLANG_DEFAULT), + [MAKEINDEX('m','l')] = MAKELANGID(LANG_NEUTRAL, SUBLANG_CUSTOM_DEFAULT), + [MAKEINDEX('m','m')] = MAKELANGID(0x55 /*LANG_BURMESE*/, SUBLANG_DEFAULT), + [MAKEINDEX('m','n')] = MAKELANGID(LANG_MONGOLIAN, SUBLANG_DEFAULT), + [MAKEINDEX('m','t')] = MAKELANGID(LANG_MALTESE, SUBLANG_DEFAULT), + [MAKEINDEX('m','v')] = MAKELANGID(LANG_DIVEHI, SUBLANG_DEFAULT), + [MAKEINDEX('m','y')] = MAKELANGID(LANG_MALAY, SUBLANG_DEFAULT), + [MAKEINDEX('n','g')] = MAKELANGID(LANG_ENGLISH, SUBLANG_CUSTOM_UNSPECIFIED), + [MAKEINDEX('n','l')] = MAKELANGID(LANG_DUTCH, SUBLANG_DEFAULT), + [MAKEINDEX('n','o')] = MAKELANGID(LANG_NORWEGIAN, SUBLANG_DEFAULT), + [MAKEINDEX('n','p')] = MAKELANGID(LANG_NEPALI, SUBLANG_DEFAULT), + [MAKEINDEX('p','h')] = MAKELANGID(LANG_FILIPINO, SUBLANG_DEFAULT), + [MAKEINDEX('p','k')] = MAKELANGID(LANG_URDU, SUBLANG_DEFAULT), + [MAKEINDEX('p','l')] = MAKELANGID(LANG_POLISH, SUBLANG_DEFAULT), + [MAKEINDEX('p','t')] = MAKELANGID(LANG_PORTUGUESE, SUBLANG_DEFAULT), + [MAKEINDEX('r','o')] = MAKELANGID(LANG_ROMANIAN, SUBLANG_DEFAULT), + [MAKEINDEX('r','s')] = MAKELANGID(LANG_SERBIAN, SUBLANG_SERBIAN_LATIN), + [MAKEINDEX('r','u')] = MAKELANGID(LANG_RUSSIAN, SUBLANG_DEFAULT), + [MAKEINDEX('s','e')] = MAKELANGID(LANG_SWEDISH, SUBLANG_DEFAULT), + [MAKEINDEX('s','i')] = MAKELANGID(LANG_SLOVENIAN, SUBLANG_DEFAULT), + [MAKEINDEX('s','k')] = MAKELANGID(LANG_SLOVAK, SUBLANG_DEFAULT), + [MAKEINDEX('s','n')] = MAKELANGID(LANG_WOLOF, SUBLANG_DEFAULT), + [MAKEINDEX('s','y')] = MAKELANGID(LANG_SYRIAC, SUBLANG_DEFAULT), + [MAKEINDEX('t','g')] = MAKELANGID(LANG_FRENCH, SUBLANG_CUSTOM_UNSPECIFIED), + [MAKEINDEX('t','h')] = MAKELANGID(LANG_THAI, SUBLANG_DEFAULT), + [MAKEINDEX('t','j')] = MAKELANGID(LANG_TAJIK, SUBLANG_DEFAULT), + [MAKEINDEX('t','m')] = MAKELANGID(LANG_TURKMEN, SUBLANG_DEFAULT), + [MAKEINDEX('t','r')] = MAKELANGID(LANG_TURKISH, SUBLANG_DEFAULT), + [MAKEINDEX('t','w')] = MAKELANGID(LANG_CHINESE, SUBLANG_CUSTOM_UNSPECIFIED), + [MAKEINDEX('t','z')] = MAKELANGID(LANG_SWAHILI, SUBLANG_CUSTOM_UNSPECIFIED), + [MAKEINDEX('u','a')] = MAKELANGID(LANG_UKRAINIAN, SUBLANG_DEFAULT), + [MAKEINDEX('u','s')] = MAKELANGID(LANG_ENGLISH, SUBLANG_DEFAULT), + [MAKEINDEX('u','z')] = MAKELANGID(LANG_UZBEK, 2), + [MAKEINDEX('v','n')] = MAKELANGID(LANG_VIETNAMESE, SUBLANG_DEFAULT), + [MAKEINDEX('z','a')] = MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_SOUTH_AFRICA), + }; + LANGID langid; + + if (layout_len == 2 && (langid = langids[MAKEINDEX(layout[0], layout[1])])) return langid; + if (layout_len == 3 && !memcmp(layout, "ara", layout_len)) return MAKELANGID(LANG_ARABIC, SUBLANG_DEFAULT); + if (layout_len == 3 && !memcmp(layout, "epo", layout_len)) return MAKELANGID(LANG_NEUTRAL, SUBLANG_CUSTOM_DEFAULT); + if (layout_len == 3 && !memcmp(layout, "mao", layout_len)) return MAKELANGID(LANG_MAORI, SUBLANG_DEFAULT); + if (layout_len == 4 && !memcmp(layout, "brai", layout_len)) return MAKELANGID(LANG_NEUTRAL, SUBLANG_CUSTOM_DEFAULT); + if (layout_len == 5 && !memcmp(layout, "latam", layout_len)) return MAKELANGID(LANG_SPANISH, SUBLANG_CUSTOM_UNSPECIFIED); +#undef MAKEINDEX + + FIXME("Unknown layout language %s\n", debugstr_a(layout)); + return MAKELANGID(LANG_NEUTRAL, SUBLANG_CUSTOM_UNSPECIFIED); +}; + +static HKL get_layout_hkl(struct layout *layout, LCID locale) +{ + if (!layout->layout_id) return (HKL)(UINT_PTR)MAKELONG(locale, layout->lang); + else return (HKL)(UINT_PTR)MAKELONG(locale, 0xf000 | layout->layout_id); +} + +static void add_xkb_layout(const char *xkb_layout, xkb_layout_index_t xkb_group, LANGID lang) +{ + static WORD next_layout_id = 1; + + struct layout *layout; + unsigned int len; + WORD index = 0; + char *ptr; + + TRACE("xkb_layout=%s xkb_group=%u lang=%04x\n", xkb_layout, xkb_group, lang); + + LIST_FOR_EACH_ENTRY(layout, &xkb_layouts, struct layout, entry) + if (layout->lang == lang) index++; + + len = strlen(xkb_layout) + 1; + if (!(layout = calloc(1, sizeof(*layout) + len))) + { + ERR("Failed to allocate memory for Xkb layout entry\n"); + return; + } + ptr = (char *)(layout + 1); + + layout->xkb_layout = strcpy(ptr, xkb_layout); + layout->xkb_group = xkb_group; + layout->lang = lang; + layout->index = index; + if (index) layout->layout_id = next_layout_id++; + + TRACE("Created layout entry=%p index=%04x lang=%04x id=%04x\n", layout, layout->index, layout->lang, layout->layout_id); + list_add_tail(&xkb_layouts, &layout->entry); +} + +static void set_current_xkb_group(xkb_layout_index_t xkb_group) +{ + struct wayland_keyboard *keyboard = &process_wayland.keyboard; + LCID locale = LOWORD(NtUserGetKeyboardLayout(0)); + struct layout *layout; + HKL hkl; + + LIST_FOR_EACH_ENTRY(layout, &xkb_layouts, struct layout, entry) + if (layout->xkb_group == xkb_group) break; + if (&layout->entry != &xkb_layouts) + hkl = get_layout_hkl(layout, locale); + else + { + ERR("Failed to find Xkb Layout for group %d\n", xkb_group); + hkl = keyboard_hkl; + } + + if (hkl == keyboard_hkl) return; + keyboard_hkl = hkl; + + TRACE("Changing keyboard layout to %p\n", hkl); + NtUserPostMessage(keyboard->focused_hwnd, WM_INPUTLANGCHANGEREQUEST, 0 /*FIXME*/, + (LPARAM)keyboard_hkl); +} + +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)) + { + if (!strcmp(name, rxkb_layout_get_description(iter))) + { + *layout = rxkb_layout_get_name(iter); + *variant = rxkb_layout_get_variant(iter); + return TRUE; + } + } + + return FALSE; +} + /********************************************************************** * Keyboard handling */ @@ -143,7 +353,9 @@ static void keyboard_handle_keymap(void *data, struct wl_keyboard *wl_keyboard, { struct wayland_keyboard *keyboard = &process_wayland.keyboard; struct xkb_keymap *xkb_keymap = NULL; + xkb_layout_index_t xkb_group; struct xkb_state *xkb_state; + struct layout *entry, *next; char *keymap_str; TRACE("format=%d fd=%d size=%d\n", format, fd, size); @@ -169,12 +381,39 @@ static void keyboard_handle_keymap(void *data, struct wl_keyboard *wl_keyboard, return; } + LIST_FOR_EACH_ENTRY_SAFE(entry, next, &xkb_layouts, struct layout, entry) + { + list_remove(&entry->entry); + free(entry); + } + + for (xkb_group = 0; xkb_group < xkb_keymap_num_layouts(xkb_keymap); xkb_group++) + { + const char *layout_name = xkb_keymap_layout_get_name(xkb_keymap, xkb_group); + const char *layout, *variant = NULL; + int layout_len, variant_len = 0; + char buffer[1024]; + LANGID lang; + + if (!find_xkb_layout_variant(layout_name, &layout, &variant)) layout = "us"; + if (variant) variant_len = strlen(variant); + layout_len = strlen(layout); + + TRACE("Found layout %u name %s -> %s:%s\n", xkb_group, layout_name, layout, variant); + + lang = langid_from_xkb_layout(layout, layout_len); + snprintf(buffer, ARRAY_SIZE(buffer), "%.*s:%.*s", layout_len, layout, variant_len, variant); + add_xkb_layout(buffer, xkb_group, lang); + } + if ((xkb_state = xkb_state_new(xkb_keymap))) { pthread_mutex_lock(&keyboard->mutex); xkb_state_unref(keyboard->xkb_state); keyboard->xkb_state = xkb_state; pthread_mutex_unlock(&keyboard->mutex); + + set_current_xkb_group(0); } xkb_keymap_unref(xkb_keymap); @@ -198,7 +437,8 @@ static void keyboard_handle_enter(void *data, struct wl_keyboard *wl_keyboard, keyboard->focused_hwnd = hwnd; pthread_mutex_unlock(&keyboard->mutex); - /* FIXME: update foreground window as well */ + NtUserPostMessage(keyboard->focused_hwnd, WM_INPUTLANGCHANGEREQUEST, 0 /*FIXME*/, + (LPARAM)keyboard_hkl); } static void keyboard_handle_leave(void *data, struct wl_keyboard *wl_keyboard, @@ -236,7 +476,7 @@ static void keyboard_handle_key(void *data, struct wl_keyboard *wl_keyboard, input.type = INPUT_KEYBOARD; input.ki.wScan = scan & 0xff; - input.ki.wVk = NtUserMapVirtualKeyEx(scan, MAPVK_VSC_TO_VK_EX, NtUserGetKeyboardLayout(0)); + input.ki.wVk = NtUserMapVirtualKeyEx(scan, MAPVK_VSC_TO_VK_EX, keyboard_hkl); if (scan & ~0xff) input.ki.dwFlags |= KEYEVENTF_EXTENDEDKEY; if (state == WL_KEYBOARD_KEY_STATE_RELEASED) input.ki.dwFlags |= KEYEVENTF_KEYUP; @@ -260,6 +500,8 @@ static void keyboard_handle_modifiers(void *data, struct wl_keyboard *wl_keyboar mods_locked, 0, 0, xkb_group); pthread_mutex_unlock(&keyboard->mutex); + set_current_xkb_group(xkb_group); + /* FIXME: Sync wine modifier state with XKB modifier state. */ } @@ -299,6 +541,13 @@ void wayland_keyboard_init(struct wl_keyboard *wl_keyboard) struct wayland_keyboard *keyboard = &process_wayland.keyboard; struct xkb_context *xkb_context = xkb_context_new(XKB_CONTEXT_NO_FLAGS); + 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; + } + if (!xkb_context) { ERR("Failed to create XKB context\n"); @@ -337,4 +586,10 @@ void wayland_keyboard_deinit(void) keyboard->xkb_state = NULL; } pthread_mutex_unlock(&keyboard->mutex); + + if (rxkb_context) + { + rxkb_context_unref(rxkb_context); + rxkb_context = NULL; + } } diff --git a/dlls/winewayland.drv/waylanddrv.h b/dlls/winewayland.drv/waylanddrv.h index 19874b36420..f5e3af562f0 100644 --- a/dlls/winewayland.drv/waylanddrv.h +++ b/dlls/winewayland.drv/waylanddrv.h @@ -28,6 +28,7 @@ #include #include #include +#include #include "viewporter-client-protocol.h" #include "xdg-output-unstable-v1-client-protocol.h" #include "xdg-shell-client-protocol.h" diff --git a/include/config.h.in b/include/config.h.in index ca13952d1ea..cda76fcfe2e 100644 --- a/include/config.h.in +++ b/include/config.h.in @@ -675,6 +675,9 @@ /* Define to 1 if you have the header file. */ #undef HAVE_XKBCOMMON_XKBCOMMON_H +/* Define to 1 if you have the header file. */ +#undef HAVE_XKBCOMMON_XKBREGISTRY_H + /* Define if Xrender has the XRenderCreateLinearGradient function */ #undef HAVE_XRENDERCREATELINEARGRADIENT