Detecting the host layout languages from their Xkb identifiers, and introducing a new driver interface using the KBDTABLES structure to translate them to Win32.
-- v7: winewayland.drv: Implement CAPLOK and SGCAPS in KBDTABLES. win32u: Support SGCAPS attributes in KBDTABLES. winewayland.drv: Add scan2vk tables for azerty, qwertz and dvorak. winewayland.drv: Translate Xkb keyboard layouts to KBDTABLES. win32u: Allow KBDTABLES conversion from CTRL + ALT to WCHAR. win32u: Force US layout in ToUnicode when CTRL is pressed. win32u: Avoid accessing NULL key name string pointer. win32u: Introduce KbdLayerDescriptor user driver entry. winewayland.drv: Enumerate Xkb layouts and create matching HKL. winewayland.drv: Handle and parse Xkb keymap events.
From: Rémi Bernon rbernon@codeweavers.com
--- dlls/winewayland.drv/wayland_keyboard.c | 56 ++++++++++++++++++++++++- dlls/winewayland.drv/waylanddrv.h | 1 + 2 files changed, 55 insertions(+), 2 deletions(-)
diff --git a/dlls/winewayland.drv/wayland_keyboard.c b/dlls/winewayland.drv/wayland_keyboard.c index e3bcce443a8..ab6aa3f1ac7 100644 --- a/dlls/winewayland.drv/wayland_keyboard.c +++ b/dlls/winewayland.drv/wayland_keyboard.c @@ -27,6 +27,7 @@
#include <linux/input.h> #undef SW_MAX /* Also defined in winuser.rh */ +#include <sys/mman.h> #include <unistd.h>
#include "waylanddrv.h" @@ -140,8 +141,43 @@ static HWND wayland_keyboard_get_focused_hwnd(void) static void keyboard_handle_keymap(void *data, struct wl_keyboard *wl_keyboard, uint32_t format, int fd, uint32_t size) { - FIXME("stub!\n"); + struct wayland_keyboard *keyboard = &process_wayland.keyboard; + struct xkb_keymap *xkb_keymap = NULL; + struct xkb_state *xkb_state; + char *keymap_str; + + TRACE("format=%d fd=%d size=%d\n", format, fd, size); + + if ((keymap_str = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0)) != MAP_FAILED) + { + if (format != WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1) + FIXME("Unsupported keymap format %#x\n", format); + else + { + xkb_keymap = xkb_keymap_new_from_string(keyboard->xkb_context, keymap_str, + XKB_KEYMAP_FORMAT_TEXT_V1, 0); + } + + munmap(keymap_str, size); + } + close(fd); + + if (!xkb_keymap) + { + ERR("Failed to load Xkb keymap\n"); + return; + } + + 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); + } + + xkb_keymap_unref(xkb_keymap); }
static void keyboard_handle_enter(void *data, struct wl_keyboard *wl_keyboard, @@ -212,8 +248,19 @@ static void keyboard_handle_modifiers(void *data, struct wl_keyboard *wl_keyboar uint32_t mods_latched, uint32_t mods_locked, uint32_t xkb_group) { - FIXME("serial=%u mods_depressed=%#x mods_latched=%#x mods_locked=%#x xkb_group=%d stub!\n", + struct wayland_keyboard *keyboard = &process_wayland.keyboard; + + if (!wayland_keyboard_get_focused_hwnd()) return; + + TRACE("serial=%u mods_depressed=%#x mods_latched=%#x mods_locked=%#x xkb_group=%d stub!\n", serial, mods_depressed, mods_latched, mods_locked, xkb_group); + + pthread_mutex_lock(&keyboard->mutex); + xkb_state_update_mask(keyboard->xkb_state, mods_depressed, mods_latched, + mods_locked, 0, 0, xkb_group); + pthread_mutex_unlock(&keyboard->mutex); + + /* FIXME: Sync wine modifier state with XKB modifier state. */ }
static void keyboard_handle_repeat_info(void *data, struct wl_keyboard *wl_keyboard, @@ -284,5 +331,10 @@ void wayland_keyboard_deinit(void) xkb_context_unref(keyboard->xkb_context); keyboard->xkb_context = NULL; } + if (keyboard->xkb_state) + { + xkb_state_unref(keyboard->xkb_state); + keyboard->xkb_state = NULL; + } pthread_mutex_unlock(&keyboard->mutex); } diff --git a/dlls/winewayland.drv/waylanddrv.h b/dlls/winewayland.drv/waylanddrv.h index 2ab2c3e8e28..19874b36420 100644 --- a/dlls/winewayland.drv/waylanddrv.h +++ b/dlls/winewayland.drv/waylanddrv.h @@ -72,6 +72,7 @@ struct wayland_keyboard { struct wl_keyboard *wl_keyboard; struct xkb_context *xkb_context; + struct xkb_state *xkb_state; HWND focused_hwnd; pthread_mutex_t mutex; };
From: Rémi Bernon rbernon@codeweavers.com
--- configure.ac | 5 +- dlls/winewayland.drv/Makefile.in | 4 +- dlls/winewayland.drv/wayland_keyboard.c | 259 +++++++++++++++++++++++- dlls/winewayland.drv/waylanddrv.h | 1 + 4 files changed, 264 insertions(+), 5 deletions(-)
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 <stdlib.h>
#include <linux/input.h> #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 <pthread.h> #include <wayland-client.h> #include <xkbcommon/xkbcommon.h> +#include <xkbcommon/xkbregistry.h> #include "viewporter-client-protocol.h" #include "xdg-output-unstable-v1-client-protocol.h" #include "xdg-shell-client-protocol.h"
From: Rémi Bernon rbernon@codeweavers.com
--- dlls/win32u/driver.c | 23 +++++++++++++++++++++++ dlls/win32u/input.c | 30 ++++++++++++++++++++++-------- include/wine/gdi_driver.h | 3 +++ 3 files changed, 48 insertions(+), 8 deletions(-)
diff --git a/dlls/win32u/driver.c b/dlls/win32u/driver.c index e6a24d1a46c..15b1df41ede 100644 --- a/dlls/win32u/driver.c +++ b/dlls/win32u/driver.c @@ -715,6 +715,15 @@ static SHORT nulldrv_VkKeyScanEx( WCHAR ch, HKL layout ) return -256; /* use default implementation */ }
+static const KBDTABLES *nulldrv_KbdLayerDescriptor( HKL layout ) +{ + return NULL; +} + +static void nulldrv_ReleaseKbdTables( const KBDTABLES *tables ) +{ +} + static UINT nulldrv_ImeProcessKey( HIMC himc, UINT wparam, UINT lparam, const BYTE *state ) { return 0; @@ -1087,6 +1096,16 @@ static SHORT loaderdrv_VkKeyScanEx( WCHAR ch, HKL layout ) return load_driver()->pVkKeyScanEx( ch, layout ); }
+static const KBDTABLES *loaderdrv_KbdLayerDescriptor( HKL layout ) +{ + return load_driver()->pKbdLayerDescriptor( layout ); +} + +static void loaderdrv_ReleaseKbdTables( const KBDTABLES *tables ) +{ + return load_driver()->pReleaseKbdTables( tables ); +} + static UINT loaderdrv_ImeProcessKey( HIMC himc, UINT wparam, UINT lparam, const BYTE *state ) { return load_driver()->pImeProcessKey( himc, wparam, lparam, state ); @@ -1213,6 +1232,8 @@ static const struct user_driver_funcs lazy_load_driver = loaderdrv_ToUnicodeEx, loaderdrv_UnregisterHotKey, loaderdrv_VkKeyScanEx, + loaderdrv_KbdLayerDescriptor, + loaderdrv_ReleaseKbdTables, loaderdrv_ImeProcessKey, loaderdrv_ImeToAsciiEx, loaderdrv_NotifyIMEStatus, @@ -1296,6 +1317,8 @@ void __wine_set_user_driver( const struct user_driver_funcs *funcs, UINT version SET_USER_FUNC(ToUnicodeEx); SET_USER_FUNC(UnregisterHotKey); SET_USER_FUNC(VkKeyScanEx); + SET_USER_FUNC(KbdLayerDescriptor); + SET_USER_FUNC(ReleaseKbdTables); SET_USER_FUNC(ImeProcessKey); SET_USER_FUNC(ImeToAsciiEx); SET_USER_FUNC(NotifyIMEStatus); diff --git a/dlls/win32u/input.c b/dlls/win32u/input.c index 3e6e440de93..d8862beb118 100644 --- a/dlls/win32u/input.c +++ b/dlls/win32u/input.c @@ -1016,13 +1016,16 @@ BOOL WINAPI NtUserSetKeyboardState( BYTE *state ) */ WORD WINAPI NtUserVkKeyScanEx( WCHAR chr, HKL layout ) { - const KBDTABLES *kbd_tables = &kbdus_tables; + const KBDTABLES *kbd_tables; SHORT ret;
TRACE_(keyboard)( "chr %s, layout %p\n", debugstr_wn(&chr, 1), layout );
if ((ret = user_driver->pVkKeyScanEx( chr, layout )) != -256) return ret; + + if (!(kbd_tables = user_driver->pKbdLayerDescriptor( layout ))) kbd_tables = &kbdus_tables; ret = kbd_tables_wchar_to_vkey( kbd_tables, chr ); + if (kbd_tables != &kbdus_tables) user_driver->pReleaseKbdTables( kbd_tables );
TRACE_(keyboard)( "ret %04x\n", ret ); return ret; @@ -1034,14 +1037,16 @@ WORD WINAPI NtUserVkKeyScanEx( WCHAR chr, HKL layout ) */ UINT WINAPI NtUserMapVirtualKeyEx( UINT code, UINT type, HKL layout ) { - const KBDTABLES *kbd_tables = &kbdus_tables; BYTE vsc2vk[0x300], vk2char[0x100]; - UINT ret; + const KBDTABLES *kbd_tables; + UINT ret = 0;
TRACE_(keyboard)( "code %u, type %u, layout %p.\n", code, type, layout );
if ((ret = user_driver->pMapVirtualKeyEx( code, type, layout )) != -1) return ret;
+ if (!(kbd_tables = user_driver->pKbdLayerDescriptor( layout ))) kbd_tables = &kbdus_tables; + switch (type) { case MAPVK_VK_TO_VSC_EX: @@ -1101,9 +1106,11 @@ UINT WINAPI NtUserMapVirtualKeyEx( UINT code, UINT type, HKL layout ) break; default: FIXME_(keyboard)( "unknown type %d\n", type ); - return 0; + break; }
+ if (kbd_tables != &kbdus_tables) user_driver->pReleaseKbdTables( kbd_tables ); + TRACE_(keyboard)( "returning 0x%04x\n", ret ); return ret; } @@ -1114,7 +1121,8 @@ UINT WINAPI NtUserMapVirtualKeyEx( UINT code, UINT type, HKL layout ) INT WINAPI NtUserGetKeyNameText( LONG lparam, WCHAR *buffer, INT size ) { INT code = ((lparam >> 16) & 0x1ff), vkey, len; - const KBDTABLES *kbd_tables = &kbdus_tables; + HKL layout = NtUserGetKeyboardLayout( 0 ); + const KBDTABLES *kbd_tables; VSC_LPWSTR *key_name;
TRACE_(keyboard)( "lparam %#x, buffer %p, size %d.\n", (int)lparam, buffer, size ); @@ -1122,6 +1130,8 @@ INT WINAPI NtUserGetKeyNameText( LONG lparam, WCHAR *buffer, INT size ) if (!buffer || !size) return 0; if ((len = user_driver->pGetKeyNameText( lparam, buffer, size )) >= 0) return len;
+ if (!(kbd_tables = user_driver->pKbdLayerDescriptor( layout ))) kbd_tables = &kbdus_tables; + if (lparam & 0x2000000) { BYTE vsc2vk[0x300]; @@ -1155,6 +1165,8 @@ INT WINAPI NtUserGetKeyNameText( LONG lparam, WCHAR *buffer, INT size ) } buffer[len] = 0;
+ if (kbd_tables != &kbdus_tables) user_driver->pReleaseKbdTables( kbd_tables ); + TRACE_(keyboard)( "ret %d, str %s.\n", len, debugstr_w(buffer) ); return len; } @@ -1165,7 +1177,7 @@ INT WINAPI NtUserGetKeyNameText( LONG lparam, WCHAR *buffer, INT size ) INT WINAPI NtUserToUnicodeEx( UINT virt, UINT scan, const BYTE *state, WCHAR *str, int size, UINT flags, HKL layout ) { - const KBDTABLES *kbd_tables = &kbdus_tables; + const KBDTABLES *kbd_tables; WCHAR buffer[2] = {0}; INT len;
@@ -1173,9 +1185,9 @@ INT WINAPI NtUserToUnicodeEx( UINT virt, UINT scan, const BYTE *state, virt, scan, state, str, size, flags, layout );
if (!state) return 0; - if ((len = user_driver->pToUnicodeEx( virt, scan, state, str, size, flags, layout )) >= -1) - return len; + if ((len = user_driver->pToUnicodeEx( virt, scan, state, str, size, flags, layout )) >= -1) return len;
+ if (!(kbd_tables = user_driver->pKbdLayerDescriptor( layout ))) kbd_tables = &kbdus_tables; if (scan & 0x8000) buffer[0] = 0; /* key up */ else buffer[0] = kbd_tables_vkey_to_wchar( kbd_tables, virt, state );
@@ -1184,6 +1196,8 @@ INT WINAPI NtUserToUnicodeEx( UINT virt, UINT scan, const BYTE *state,
lstrcpynW( str, buffer, size );
+ if (kbd_tables != &kbdus_tables) user_driver->pReleaseKbdTables( kbd_tables ); + TRACE_(keyboard)( "ret %d, str %s.\n", len, debugstr_w(str) ); return len; } diff --git a/include/wine/gdi_driver.h b/include/wine/gdi_driver.h index aa59a256482..c5803fd7228 100644 --- a/include/wine/gdi_driver.h +++ b/include/wine/gdi_driver.h @@ -29,6 +29,7 @@ #include "ntuser.h" #include "immdev.h" #include "ddk/d3dkmthk.h" +#include "kbd.h" #include "wine/list.h"
struct gdi_dc_funcs; @@ -292,6 +293,8 @@ struct user_driver_funcs INT (*pToUnicodeEx)(UINT,UINT,const BYTE *,LPWSTR,int,UINT,HKL); void (*pUnregisterHotKey)(HWND, UINT, UINT); SHORT (*pVkKeyScanEx)(WCHAR, HKL); + const KBDTABLES *(*pKbdLayerDescriptor)(HKL); + void (*pReleaseKbdTables)(const KBDTABLES *); /* IME functions */ UINT (*pImeProcessKey)(HIMC,UINT,UINT,const BYTE*); UINT (*pImeToAsciiEx)(UINT,UINT,const BYTE*,COMPOSITIONSTRING*,HIMC);
From: Rémi Bernon rbernon@codeweavers.com
The host layout behave very differently in that case and we have tests that check the exact results. --- dlls/win32u/input.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/dlls/win32u/input.c b/dlls/win32u/input.c index f718b274176..ae03f99adc9 100644 --- a/dlls/win32u/input.c +++ b/dlls/win32u/input.c @@ -516,6 +516,11 @@ static WCHAR kbd_tables_vkey_to_wchar( const KBDTABLES *tables, UINT vkey, const
if (ctrl && alt) return WCH_NONE; if (!ctrl && vkey == VK_ESCAPE) return VK_ESCAPE; + if (ctrl && !alt) + { + if (vkey >= 'A' && vkey <= 'Z') return vkey - 'A' + 1; + tables = &kbdus_tables; + }
mod = caps_mod = kbd_tables_get_mod_num( tables, state, FALSE ); if (caps) caps_mod = kbd_tables_get_mod_num( tables, state, TRUE ); @@ -531,7 +536,6 @@ static WCHAR kbd_tables_vkey_to_wchar( const KBDTABLES *tables, UINT vkey, const } }
- if (ctrl && vkey >= 'A' && vkey <= 'Z') return vkey - 'A' + 1; return WCH_NONE; }
From: Rémi Bernon rbernon@codeweavers.com
--- dlls/win32u/input.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/dlls/win32u/input.c b/dlls/win32u/input.c index d8862beb118..f718b274176 100644 --- a/dlls/win32u/input.c +++ b/dlls/win32u/input.c @@ -1151,7 +1151,7 @@ INT WINAPI NtUserGetKeyNameText( LONG lparam, WCHAR *buffer, INT size ) else key_name = kbd_tables->pKeyNamesExt; while (key_name->vsc && key_name->vsc != (BYTE)code) key_name++;
- if (key_name->vsc == (BYTE)code) + if (key_name->vsc == (BYTE)code && key_name->pwsz) { len = min( size - 1, wcslen( key_name->pwsz ) ); memcpy( buffer, key_name->pwsz, len * sizeof(WCHAR) );
From: Rémi Bernon rbernon@codeweavers.com
--- dlls/win32u/input.c | 2 +- include/kbd.h | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-)
diff --git a/dlls/win32u/input.c b/dlls/win32u/input.c index ae03f99adc9..fa64de881cf 100644 --- a/dlls/win32u/input.c +++ b/dlls/win32u/input.c @@ -514,7 +514,7 @@ static WCHAR kbd_tables_vkey_to_wchar( const KBDTABLES *tables, UINT vkey, const ctrl = state[VK_CONTROL] & 0x80; caps = state[VK_CAPITAL] & 1;
- if (ctrl && alt) return WCH_NONE; + if (ctrl && alt && !(tables->fLocaleFlags & KLLF_ALTGR)) return WCH_NONE; if (!ctrl && vkey == VK_ESCAPE) return VK_ESCAPE; if (ctrl && !alt) { diff --git a/include/kbd.h b/include/kbd.h index 9bbcd886b1c..da805b7cefc 100644 --- a/include/kbd.h +++ b/include/kbd.h @@ -40,6 +40,10 @@ #define KANALOK 0x08 #define GRPSELTAP 0x80
+#define KLLF_ALTGR 0x0001 +#define KLLF_SHIFTLOCK 0x0002 +#define KLLF_LRM_RLM 0x0004 + typedef struct { BYTE Vk;
From: Rémi Bernon rbernon@codeweavers.com
--- dlls/winewayland.drv/wayland_keyboard.c | 281 +++++++++++++++++++++++- dlls/winewayland.drv/waylanddrv.h | 2 + dlls/winewayland.drv/waylanddrv_main.c | 2 + 3 files changed, 277 insertions(+), 8 deletions(-)
diff --git a/dlls/winewayland.drv/wayland_keyboard.c b/dlls/winewayland.drv/wayland_keyboard.c index 847810e9bb2..2afaf323bed 100644 --- a/dlls/winewayland.drv/wayland_keyboard.c +++ b/dlls/winewayland.drv/wayland_keyboard.c @@ -41,19 +41,84 @@ struct layout { struct list entry; char *xkb_layout; + LONG ref;
int xkb_group; LANGID lang; WORD index; /* "Layout Id", used by NtUserGetKeyboardLayoutName / LoadKeyboardLayoutW */ WORD layout_id; + + KBDTABLES tables; + VSC_LPWSTR key_names[0x100]; + VSC_LPWSTR key_names_ext[0x200]; + WCHAR *key_names_str; + + USHORT vsc2vk[0x100]; + VSC_VK vsc2vk_e0[0x100]; + VSC_VK vsc2vk_e1[0x100]; + + VK_TO_WCHAR_TABLE vk_to_wchar_table[2]; + VK_TO_WCHARS8 vk_to_wchars8[0x100]; + VK_TO_BIT vk2bit[4]; + union + { + MODIFIERS modifiers; + char modifiers_buf[offsetof(MODIFIERS, ModNumber[8])]; + }; };
-/* These are only used from the wayland event thread and don't need locking */ +static pthread_mutex_t xkb_layouts_mutex = PTHREAD_MUTEX_INITIALIZER; static struct list xkb_layouts = LIST_INIT(xkb_layouts); + +/* These are only used from the wayland event thread and don't need locking */ static struct rxkb_context *rxkb_context; static HKL keyboard_hkl; /* the HKL matching the currently active xkb group */
+static void xkb_layout_addref(struct layout *layout) +{ + InterlockedIncrement(&layout->ref); +} + +static void xkb_layout_release(struct layout *layout) +{ + if (!InterlockedDecrement(&layout->ref)) + free(layout); +} + +#define EXTRA_SCAN2VK \ + T36 | KBDEXT, T37 | KBDMULTIVK, \ + T38, T39, T3A, T3B, T3C, T3D, T3E, T3F, \ + T40, T41, T42, T43, T44, T45 | KBDEXT | KBDMULTIVK, T46 | KBDMULTIVK, T47 | KBDNUMPAD | KBDSPECIAL, \ + T48 | KBDNUMPAD | KBDSPECIAL, T49 | KBDNUMPAD | KBDSPECIAL, T4A, T4B | KBDNUMPAD | KBDSPECIAL, \ + T4C | KBDNUMPAD | KBDSPECIAL, T4D | KBDNUMPAD | KBDSPECIAL, T4E, T4F | KBDNUMPAD | KBDSPECIAL, \ + T50 | KBDNUMPAD | KBDSPECIAL, T51 | KBDNUMPAD | KBDSPECIAL, T52 | KBDNUMPAD | KBDSPECIAL, \ + T53 | KBDNUMPAD | KBDSPECIAL, T54, T55, T56, T57, \ + T58, T59, T5A, T5B, T5C, T5D, T5E, T5F, \ + T60, T61, T62, T63, T64, T65, T66, T67, \ + T68, T69, T6A, T6B, T6C, T6D, T6E, T6F, \ + T70, T71, T72, T73, T74, T75, T76, T77, \ + T78, T79, T7A, T7B, T7C, T7D, T7E, \ + [0x110] = X10 | KBDEXT, [0x119] = X19 | KBDEXT, [0x11d] = X1D | KBDEXT, [0x120] = X20 | KBDEXT, \ + [0x121] = X21 | KBDEXT, [0x122] = X22 | KBDEXT, [0x124] = X24 | KBDEXT, [0x12e] = X2E | KBDEXT, \ + [0x130] = X30 | KBDEXT, [0x132] = X32 | KBDEXT, [0x135] = X35 | KBDEXT, [0x137] = X37 | KBDEXT, \ + [0x138] = X38 | KBDEXT, [0x147] = X47 | KBDEXT, [0x148] = X48 | KBDEXT, [0x149] = X49 | KBDEXT, \ + [0x14b] = X4B | KBDEXT, [0x14d] = X4D | KBDEXT, [0x14f] = X4F | KBDEXT, [0x150] = X50 | KBDEXT, \ + [0x151] = X51 | KBDEXT, [0x152] = X52 | KBDEXT, [0x153] = X53 | KBDEXT, [0x15b] = X5B | KBDEXT, \ + [0x15c] = X5C | KBDEXT, [0x15d] = X5D | KBDEXT, [0x15f] = X5F | KBDEXT, [0x165] = X65 | KBDEXT, \ + [0x166] = X66 | KBDEXT, [0x167] = X67 | KBDEXT, [0x168] = X68 | KBDEXT, [0x169] = X69 | KBDEXT, \ + [0x16a] = X6A | KBDEXT, [0x16b] = X6B | KBDEXT, [0x16c] = X6C | KBDEXT, [0x16d] = X6D | KBDEXT, \ + [0x11c] = X1C | KBDEXT, [0x146] = X46 | KBDEXT, [0x21d] = Y1D, + +static const USHORT scan2vk_qwerty[0x280] = +{ + T00, T01, T02, T03, T04, T05, T06, T07, T08, T09, T0A, T0B, T0C, T0D, T0E, + T0F, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T1A, T1B, T1C, + T1D, T1E, T1F, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29, + T2A, T2B, T2C, T2D, T2E, T2F, T30, T31, T32, T33, T34, T35, + EXTRA_SCAN2VK +}; + static WORD key2scan(UINT key) { /* base keys can be mapped directly */ @@ -258,33 +323,173 @@ static HKL get_layout_hkl(struct layout *layout, LCID locale) 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 void add_xkb_layout(const char *xkb_layout, struct xkb_keymap *xkb_keymap, + xkb_layout_index_t xkb_group, LANGID lang) { static WORD next_layout_id = 1;
+ unsigned int mod, keyc, len, names_len, min_keycode, max_keycode; + struct xkb_state *xkb_state = xkb_state_new(xkb_keymap); + xkb_mod_mask_t shift_mask, control_mask, altgr_mask; + VSC_LPWSTR *names_entry, *names_ext_entry; + VSC_VK *vsc2vk_e0_entry, *vsc2vk_e1_entry; + VK_TO_WCHARS8 *vk2wchars_entry; struct layout *layout; - unsigned int len; + const USHORT *scan2vk; + WCHAR *names_str; WORD index = 0; char *ptr;
- TRACE("xkb_layout=%s xkb_group=%u lang=%04x\n", xkb_layout, xkb_group, lang); + min_keycode = xkb_keymap_min_keycode(xkb_keymap); + max_keycode = xkb_keymap_max_keycode(xkb_keymap); + + TRACE("xkb_layout=%s xkb_keymap=%p xkb_group=%u lang=%04x\n", xkb_layout, xkb_keymap, xkb_group, lang);
LIST_FOR_EACH_ENTRY(layout, &xkb_layouts, struct layout, entry) if (layout->lang == lang) index++;
+ for (names_len = 0, keyc = min_keycode; keyc <= max_keycode; keyc++) + { + const xkb_keysym_t *keysym; + if (!xkb_keymap_key_get_syms_by_level(xkb_keymap, keyc, xkb_group, 0, &keysym)) continue; + names_len += xkb_keysym_get_name(*keysym, NULL, 0) + 1; + } + + names_len *= sizeof(WCHAR); len = strlen(xkb_layout) + 1; - if (!(layout = calloc(1, sizeof(*layout) + len))) + if (!(layout = calloc(1, sizeof(*layout) + names_len + len))) { ERR("Failed to allocate memory for Xkb layout entry\n"); return; } - ptr = (char *)(layout + 1); + layout->ref = 1;
+ ptr = (char *)(layout + 1); layout->xkb_layout = strcpy(ptr, xkb_layout); + ptr += len; + layout->xkb_group = xkb_group; layout->lang = lang; layout->index = index; if (index) layout->layout_id = next_layout_id++; + layout->key_names_str = names_str = (void *)ptr; + scan2vk = scan2vk_qwerty; + + layout->tables.pKeyNames = layout->key_names; + layout->tables.pKeyNamesExt = layout->key_names_ext; + layout->tables.bMaxVSCtoVK = 0xff; + layout->tables.pusVSCtoVK = layout->vsc2vk; + layout->tables.pVSCtoVK_E0 = layout->vsc2vk_e0; + layout->tables.pVSCtoVK_E1 = layout->vsc2vk_e1; + layout->tables.pCharModifiers = &layout->modifiers; + layout->tables.pVkToWcharTable = layout->vk_to_wchar_table; + layout->tables.fLocaleFlags = MAKELONG(KLLF_ALTGR, KBD_VERSION); + + layout->vk_to_wchar_table[0].pVkToWchars = (VK_TO_WCHARS1 *)layout->vk_to_wchars8; + layout->vk_to_wchar_table[0].cbSize = sizeof(*layout->vk_to_wchars8); + layout->vk_to_wchar_table[0].nModifications = 8; + + layout->vk2bit[0].Vk = VK_SHIFT; + layout->vk2bit[0].ModBits = KBDSHIFT; + layout->vk2bit[1].Vk = VK_CONTROL; + layout->vk2bit[1].ModBits = KBDCTRL; + layout->vk2bit[2].Vk = VK_MENU; + layout->vk2bit[2].ModBits = KBDALT; + + layout->modifiers.pVkToBit = layout->vk2bit; + for (mod = 0; mod <= (KBDSHIFT | KBDCTRL | KBDALT); ++mod) + { + BYTE num = 0; + if (mod & KBDSHIFT) num |= 1 << 0; + if (mod & KBDCTRL) num |= 1 << 1; + if (mod & KBDALT) num |= 1 << 2; + layout->modifiers.ModNumber[mod] = num; + } + layout->modifiers.wMaxModBits = 7; + + names_entry = layout->tables.pKeyNames; + names_ext_entry = layout->tables.pKeyNamesExt; + vsc2vk_e0_entry = layout->tables.pVSCtoVK_E0; + vsc2vk_e1_entry = layout->tables.pVSCtoVK_E1; + vk2wchars_entry = layout->vk_to_wchars8; + + for (keyc = min_keycode; keyc <= max_keycode; keyc++) + { + WORD scan = key2scan(keyc - 8); + const xkb_keysym_t *keysym; + VSC_LPWSTR *entry; + char name[256]; + + if (!xkb_keymap_key_get_syms_by_level(xkb_keymap, keyc, xkb_group, 0, &keysym)) continue; + len = xkb_keysym_get_name(*keysym, name, sizeof(name)); + + if (!(scan & 0xff) || !len) continue; + if (!(scan & 0x300)) entry = names_entry++; + else entry = names_ext_entry++; + + entry->vsc = (BYTE)scan; + entry->pwsz = names_str; + names_str += ntdll_umbstowcs(name, len + 1, entry->pwsz, len + 1); + + TRACE("keyc %#04x, scan %#04x -> name %s\n", keyc, scan, debugstr_w(entry->pwsz)); + } + + for (keyc = min_keycode; keyc <= max_keycode; keyc++) + { + WORD scan = key2scan(keyc - 8), vkey = scan2vk[scan]; + VSC_VK *entry = NULL; + + if (!(scan & 0xff) || !vkey) continue; + if (scan & 0x100) entry = vsc2vk_e0_entry++; + else if (scan & 0x200) entry = vsc2vk_e1_entry++; + else layout->tables.pusVSCtoVK[scan & 0xff] = vkey; + + if (entry) + { + entry->Vsc = scan & 0xff; + entry->Vk = vkey; + } + + TRACE("keyc %#04x, scan %#05x -> vkey %#06x\n", keyc, scan, vkey); + } + + shift_mask = 1 << xkb_keymap_mod_get_index(xkb_keymap, XKB_MOD_NAME_SHIFT); + control_mask = 1 << xkb_keymap_mod_get_index(xkb_keymap, XKB_MOD_NAME_CTRL); + altgr_mask = 1 << xkb_keymap_mod_get_index(xkb_keymap, "Mod5"); + + for (keyc = min_keycode; keyc <= max_keycode; keyc++) + { + WORD scan = key2scan(keyc - 8), vkey = scan2vk[scan]; + VK_TO_WCHARS8 vkey2wch = {.VirtualKey = vkey}; + BOOL found = FALSE; + unsigned int mod; + + for (mod = 0; mod < 8; ++mod) + { + xkb_mod_mask_t mod_mask = 0; + uint32_t ret; + + if (mod & (1 << 0)) mod_mask |= shift_mask; + if (mod & (1 << 1)) mod_mask |= control_mask; + /* Windows uses VK_CTRL + VK_MENU for AltGr, we cannot combine Ctrl and Alt */ + if (mod & (1 << 2)) mod_mask = (mod_mask & ~control_mask) | altgr_mask; + + xkb_state_update_mask(xkb_state, 0, 0, mod_mask, 0, 0, xkb_group); + + if (mod_mask & control_mask) vkey2wch.wch[mod] = WCH_NONE; /* on Windows CTRL+key behave specifically */ + else if (!(ret = xkb_state_key_get_utf32(xkb_state, keyc))) vkey2wch.wch[mod] = WCH_NONE; + else vkey2wch.wch[mod] = ret; + + if (vkey2wch.wch[mod] != WCH_NONE) found = TRUE; + } + + if (!found) continue; + + TRACE("vkey %#06x -> %s\n", vkey2wch.VirtualKey, debugstr_wn(vkey2wch.wch, 8)); + *vk2wchars_entry++ = vkey2wch; + } + + xkb_state_unref(xkb_state);
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); @@ -297,6 +502,8 @@ static void set_current_xkb_group(xkb_layout_index_t xkb_group) struct layout *layout; HKL hkl;
+ pthread_mutex_lock(&xkb_layouts_mutex); + LIST_FOR_EACH_ENTRY(layout, &xkb_layouts, struct layout, entry) if (layout->xkb_group == xkb_group) break; if (&layout->entry != &xkb_layouts) @@ -307,6 +514,8 @@ static void set_current_xkb_group(xkb_layout_index_t xkb_group) hkl = keyboard_hkl; }
+ pthread_mutex_unlock(&xkb_layouts_mutex); + if (hkl == keyboard_hkl) return; keyboard_hkl = hkl;
@@ -381,10 +590,12 @@ static void keyboard_handle_keymap(void *data, struct wl_keyboard *wl_keyboard, return; }
+ pthread_mutex_lock(&xkb_layouts_mutex); + LIST_FOR_EACH_ENTRY_SAFE(entry, next, &xkb_layouts, struct layout, entry) { list_remove(&entry->entry); - free(entry); + xkb_layout_release(entry); }
for (xkb_group = 0; xkb_group < xkb_keymap_num_layouts(xkb_keymap); xkb_group++) @@ -403,9 +614,11 @@ static void keyboard_handle_keymap(void *data, struct wl_keyboard *wl_keyboard,
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); + add_xkb_layout(buffer, xkb_keymap, xkb_group, lang); }
+ pthread_mutex_unlock(&xkb_layouts_mutex); + if ((xkb_state = xkb_state_new(xkb_keymap))) { pthread_mutex_lock(&keyboard->mutex); @@ -462,6 +675,17 @@ static void keyboard_handle_leave(void *data, struct wl_keyboard *wl_keyboard, /* FIXME: update foreground window as well */ }
+static void send_right_control(HWND hwnd, uint32_t state) +{ + INPUT input = {0}; + input.type = INPUT_KEYBOARD; + input.ki.wScan = key2scan(KEY_RIGHTCTRL); + input.ki.wVk = VK_RCONTROL; + input.ki.dwFlags |= KEYEVENTF_EXTENDEDKEY; + if (state == WL_KEYBOARD_KEY_STATE_RELEASED) input.ki.dwFlags |= KEYEVENTF_KEYUP; + __wine_send_input(hwnd, &input, NULL); +} + static void keyboard_handle_key(void *data, struct wl_keyboard *wl_keyboard, uint32_t serial, uint32_t time, uint32_t key, uint32_t state) @@ -474,6 +698,9 @@ static void keyboard_handle_key(void *data, struct wl_keyboard *wl_keyboard,
TRACE_(key)("serial=%u hwnd=%p key=%d scan=%#x state=%#x\n", serial, hwnd, key, scan, state);
+ /* NOTE: Windows normally sends VK_CONTROL + VK_MENU only if the layout has KLLF_ALTGR */ + if (key == KEY_RIGHTALT) send_right_control(hwnd, state); + input.type = INPUT_KEYBOARD; input.ki.wScan = scan & 0xff; input.ki.wVk = NtUserMapVirtualKeyEx(scan, MAPVK_VSC_TO_VK_EX, keyboard_hkl); @@ -593,3 +820,41 @@ void wayland_keyboard_deinit(void) rxkb_context = NULL; } } + +/*********************************************************************** + * KbdLayerDescriptor (WAYLANDDRV.@) + */ +const KBDTABLES *WAYLAND_KbdLayerDescriptor(HKL hkl) +{ + struct layout *layout; + + TRACE("hkl=%p\n", hkl); + + pthread_mutex_lock(&xkb_layouts_mutex); + + LIST_FOR_EACH_ENTRY(layout, &xkb_layouts, struct layout, entry) + if (hkl == get_layout_hkl(layout, LOWORD(hkl))) break; + if (&layout->entry == &xkb_layouts) layout = NULL; + else xkb_layout_addref(layout); + + pthread_mutex_unlock(&xkb_layouts_mutex); + + if (!layout) + { + WARN("Failed to find Xkb layout for HKL %p\n", hkl); + return NULL; + } + + TRACE("Found layout entry %p, hkl %04x%04x id %04x\n", + layout, layout->index, layout->lang, layout->layout_id); + return &layout->tables; +} + +/*********************************************************************** + * ReleaseKbdTables (WAYLANDDRV.@) + */ +void WAYLAND_ReleaseKbdTables(const KBDTABLES *tables) +{ + struct layout *layout = CONTAINING_RECORD(tables, struct layout, tables); + xkb_layout_release(layout); +} diff --git a/dlls/winewayland.drv/waylanddrv.h b/dlls/winewayland.drv/waylanddrv.h index f5e3af562f0..0e781352519 100644 --- a/dlls/winewayland.drv/waylanddrv.h +++ b/dlls/winewayland.drv/waylanddrv.h @@ -270,6 +270,8 @@ void wayland_window_flush(HWND hwnd);
void wayland_keyboard_init(struct wl_keyboard *wl_keyboard); void wayland_keyboard_deinit(void); +const KBDTABLES *WAYLAND_KbdLayerDescriptor(HKL hkl); +void WAYLAND_ReleaseKbdTables(const KBDTABLES *);
/********************************************************************** * Wayland pointer diff --git a/dlls/winewayland.drv/waylanddrv_main.c b/dlls/winewayland.drv/waylanddrv_main.c index be898f9e2c3..435a6d2b36c 100644 --- a/dlls/winewayland.drv/waylanddrv_main.c +++ b/dlls/winewayland.drv/waylanddrv_main.c @@ -33,6 +33,8 @@ static const struct user_driver_funcs waylanddrv_funcs = { .pDesktopWindowProc = WAYLAND_DesktopWindowProc, .pDestroyWindow = WAYLAND_DestroyWindow, + .pKbdLayerDescriptor = WAYLAND_KbdLayerDescriptor, + .pReleaseKbdTables = WAYLAND_ReleaseKbdTables, .pSetCursor = WAYLAND_SetCursor, .pSysCommand = WAYLAND_SysCommand, .pUpdateDisplayDevices = WAYLAND_UpdateDisplayDevices,
From: Rémi Bernon rbernon@codeweavers.com
--- dlls/winewayland.drv/wayland_keyboard.c | 37 ++++++++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-)
diff --git a/dlls/winewayland.drv/wayland_keyboard.c b/dlls/winewayland.drv/wayland_keyboard.c index 2afaf323bed..3230ca2f264 100644 --- a/dlls/winewayland.drv/wayland_keyboard.c +++ b/dlls/winewayland.drv/wayland_keyboard.c @@ -119,6 +119,33 @@ static const USHORT scan2vk_qwerty[0x280] = EXTRA_SCAN2VK };
+static const USHORT scan2vk_azerty[0x280] = +{ + T00, T01, T02, T03, T04, T05, T06, T07, T08, T09, T0A, T0B, VK_OEM_4, T0D, T0E, + T0F, 'A', 'Z', T12, T13, T14, T15, T16, T17, T18, T19, VK_OEM_6, VK_OEM_1, T1C, + T1D, 'Q', T1F, T20, T21, T22, T23, T24, T25, T26, 'M', VK_OEM_3, VK_OEM_7, + T2A, T2B, 'W', T2D, T2E, T2F, T30, T31, VK_OEM_COMMA, VK_OEM_PERIOD, VK_OEM_2, VK_OEM_8, + EXTRA_SCAN2VK +}; + +static const USHORT scan2vk_qwertz[0x280] = +{ + T00, T01, T02, T03, T04, T05, T06, T07, T08, T09, T0A, T0B, VK_OEM_4, VK_OEM_6, T0E, + T0F, T10, T11, T12, T13, T14, 'Z', T16, T17, T18, T19, VK_OEM_1, VK_OEM_3, T1C, + T1D, T1E, T1F, T20, T21, T22, T23, T24, T25, T26, VK_OEM_7, VK_OEM_5, VK_OEM_2, + T2A, VK_OEM_8, 'Y', T2D, T2E, T2F, T30, T31, T32, T33, T34, VK_OEM_MINUS, + EXTRA_SCAN2VK +}; + +static const USHORT scan2vk_dvorak[0x280] = +{ + T00, T01, T02, T03, T04, T05, T06, T07, T08, T09, T0A, T0B, VK_OEM_4, VK_OEM_6, T0E, + T0F, VK_OEM_7, VK_OEM_COMMA, VK_OEM_PERIOD, 'P', 'Y', 'F', 'G', 'C', 'R', 'L', VK_OEM_2, VK_OEM_PLUS, T1C, + T1D, T1E, 'O', 'E', 'U', 'I', 'D', 'H', 'T', 'N', 'S', VK_OEM_MINUS, T29, + T2A, T2B, VK_OEM_1, 'Q', 'J', 'K', 'X', 'B', 'M', 'W', 'V', 'Z', + EXTRA_SCAN2VK +}; + static WORD key2scan(UINT key) { /* base keys can be mapped directly */ @@ -373,7 +400,15 @@ static void add_xkb_layout(const char *xkb_layout, struct xkb_keymap *xkb_keymap layout->index = index; if (index) layout->layout_id = next_layout_id++; layout->key_names_str = names_str = (void *)ptr; - scan2vk = scan2vk_qwerty; + + switch (lang) + { + case MAKELANGID(LANG_FRENCH, SUBLANG_DEFAULT): scan2vk = scan2vk_azerty; + case MAKELANGID(LANG_GERMAN, SUBLANG_DEFAULT): scan2vk = scan2vk_qwertz; + case MAKELANGID(LANG_GERMAN, SUBLANG_GERMAN_SWISS): scan2vk = scan2vk_qwertz; + default: scan2vk = scan2vk_qwerty; + } + if (strstr(xkb_layout, "dvorak")) scan2vk = scan2vk_dvorak;
layout->tables.pKeyNames = layout->key_names; layout->tables.pKeyNamesExt = layout->key_names_ext;
From: Rémi Bernon rbernon@codeweavers.com
--- dlls/win32u/input.c | 5 +++++ 1 file changed, 5 insertions(+)
diff --git a/dlls/win32u/input.c b/dlls/win32u/input.c index fa64de881cf..3ee46f0bfcf 100644 --- a/dlls/win32u/input.c +++ b/dlls/win32u/input.c @@ -531,6 +531,11 @@ static WCHAR kbd_tables_vkey_to_wchar( const KBDTABLES *tables, UINT vkey, const for (entry = table->pVkToWchars; entry->VirtualKey; entry = NEXT_ENTRY(table, entry)) { if (entry->VirtualKey != vkey) continue; + /* SGCAPS attribute may be set on entries where VK_CAPITAL and VK_SHIFT behave differently. + * The entry corresponds to the mapping when Caps Lock is on, and a second entry follows it + * with the mapping when Caps Lock is off. + */ + if ((entry->Attributes & SGCAPS) && !caps) entry = NEXT_ENTRY(table, entry); if ((entry->Attributes & CAPLOK) && table->nModifications > caps_mod) return entry->wch[caps_mod]; return entry->wch[mod]; }
From: Rémi Bernon rbernon@codeweavers.com
--- dlls/winewayland.drv/wayland_keyboard.c | 34 ++++++++++++++++++++++--- 1 file changed, 30 insertions(+), 4 deletions(-)
diff --git a/dlls/winewayland.drv/wayland_keyboard.c b/dlls/winewayland.drv/wayland_keyboard.c index 3230ca2f264..26cf3e8a7af 100644 --- a/dlls/winewayland.drv/wayland_keyboard.c +++ b/dlls/winewayland.drv/wayland_keyboard.c @@ -59,7 +59,7 @@ struct layout VSC_VK vsc2vk_e1[0x100];
VK_TO_WCHAR_TABLE vk_to_wchar_table[2]; - VK_TO_WCHARS8 vk_to_wchars8[0x100]; + VK_TO_WCHARS8 vk_to_wchars8[0x100 * 2 /* SGCAPS */]; VK_TO_BIT vk2bit[4]; union { @@ -357,7 +357,7 @@ static void add_xkb_layout(const char *xkb_layout, struct xkb_keymap *xkb_keymap
unsigned int mod, keyc, len, names_len, min_keycode, max_keycode; struct xkb_state *xkb_state = xkb_state_new(xkb_keymap); - xkb_mod_mask_t shift_mask, control_mask, altgr_mask; + xkb_mod_mask_t shift_mask, control_mask, altgr_mask, capslock_mask; VSC_LPWSTR *names_entry, *names_ext_entry; VSC_VK *vsc2vk_e0_entry, *vsc2vk_e1_entry; VK_TO_WCHARS8 *vk2wchars_entry; @@ -490,13 +490,15 @@ static void add_xkb_layout(const char *xkb_layout, struct xkb_keymap *xkb_keymap
shift_mask = 1 << xkb_keymap_mod_get_index(xkb_keymap, XKB_MOD_NAME_SHIFT); control_mask = 1 << xkb_keymap_mod_get_index(xkb_keymap, XKB_MOD_NAME_CTRL); + capslock_mask = 1 << xkb_keymap_mod_get_index(xkb_keymap, XKB_MOD_NAME_CAPS); altgr_mask = 1 << xkb_keymap_mod_get_index(xkb_keymap, "Mod5");
for (keyc = min_keycode; keyc <= max_keycode; keyc++) { WORD scan = key2scan(keyc - 8), vkey = scan2vk[scan]; - VK_TO_WCHARS8 vkey2wch = {.VirtualKey = vkey}; - BOOL found = FALSE; + VK_TO_WCHARS8 vkey2wch = {.VirtualKey = vkey}, caps_vkey2wch = vkey2wch; + BOOL found = FALSE, caps_found = FALSE; + uint32_t caps_ret, shift_ret; unsigned int mod;
for (mod = 0; mod < 8; ++mod) @@ -516,10 +518,34 @@ static void add_xkb_layout(const char *xkb_layout, struct xkb_keymap *xkb_keymap else vkey2wch.wch[mod] = ret;
if (vkey2wch.wch[mod] != WCH_NONE) found = TRUE; + + xkb_state_update_mask(xkb_state, 0, 0, mod_mask | capslock_mask, 0, 0, xkb_group); + + if (mod_mask & control_mask) caps_vkey2wch.wch[mod] = WCH_NONE; /* on Windows CTRL+key behave specifically */ + else if (!(ret = xkb_state_key_get_utf32(xkb_state, keyc))) caps_vkey2wch.wch[mod] = WCH_NONE; + else if (ret == vkey2wch.wch[mod]) caps_vkey2wch.wch[mod] = WCH_NONE; + else caps_vkey2wch.wch[mod] = ret; + + if (caps_vkey2wch.wch[mod] != WCH_NONE) caps_found = TRUE; }
if (!found) continue;
+ if (caps_found) + { + TRACE("vkey %#06x + CAPS -> %s\n", caps_vkey2wch.VirtualKey, debugstr_wn(caps_vkey2wch.wch, 8)); + caps_vkey2wch.Attributes = SGCAPS; + *vk2wchars_entry++ = caps_vkey2wch; + } + else + { + xkb_state_update_mask(xkb_state, 0, 0, capslock_mask, 0, 0, xkb_group); + caps_ret = xkb_state_key_get_utf32(xkb_state, keyc); + xkb_state_update_mask(xkb_state, 0, 0, shift_mask, 0, 0, xkb_group); + shift_ret = xkb_state_key_get_utf32(xkb_state, keyc); + if (caps_ret && caps_ret == shift_ret) vkey2wch.Attributes |= CAPLOK; + } + TRACE("vkey %#06x -> %s\n", vkey2wch.VirtualKey, debugstr_wn(vkey2wch.wch, 8)); *vk2wchars_entry++ = vkey2wch; }
Rebased with DECLSPEC_HIDDEN removed.