@afrantzis Can't seem to be possible to add you as a reviewer, probably you need to request "Access" to the wine/wine project (near the top of the project page).
What do you think of something like that? It is very different from the current winex11 code, but I believe it will match keyboard layouts in a much more accurate way, and it's also IMO much simpler. If that works well with Wayland, I think it could be a good hint that it might work as well in X11 and make a case for my other MR to use that approach there.
I was a bit annoyed that it doesn't seem possible to retrieve the Xkb "layout:variant" string here, but only the layout description, so I had to use xkbregistry to match it back to the known layouts.
It is mostly only there to provide a more accurate HKL value (which should match the layout langid), and scan to vk mapping table, and custom layouts should still work. The lang would be neutral then, and the scan to vk table is QWERTY by default, which is the most common case, and doesn't enforce any vkey -> unicode mapping anyway. So, if the xkbregistry dependency is an issue we could probably make it optional and dynamically loaded, and skip the langid and scan to vk specialized mappings.
-- v2: winewayland.drv: Implement Xkb composition using ImeProcessKey. winewayland.drv: Translate Xkb keyboard layouts to KBDTABLES. win32u: Allow KBDTABLES conversion from CTLR + ALT to WCHAR. win32u: Force US layout in ToUnicode when CTRL is pressed. win32u: Introduce KbdLayerDescriptor user driver entry. win32u: Avoid accessing NULL key name string pointer. winewayland.drv: Enumerate Xkb layouts and create matching HKL. winewayland.drv: Handle and parse Xkb keymap events. win32u: Implement opt-in auto-repeat for WM_(SYS)KEYDOWN messages. winewayland.drv: Configure win32u keyboard repeat delay and speed. winewayland.drv: Basic handling of Wayland keyboard events. gitlab: Install libxkbcommon and libxkbregistry dependencies.
From: Rémi Bernon rbernon@codeweavers.com
--- tools/gitlab/image.docker | 2 ++ 1 file changed, 2 insertions(+)
diff --git a/tools/gitlab/image.docker b/tools/gitlab/image.docker index 50607d13872..8ff9055df2f 100644 --- a/tools/gitlab/image.docker +++ b/tools/gitlab/image.docker @@ -42,6 +42,8 @@ RUN export DEBIAN_FRONTEND=noninteractive; \ libxext-dev:amd64 libxext-dev:i386 \ libxi-dev:amd64 libxi-dev:i386 \ libxinerama-dev:amd64 libxinerama-dev:i386 \ + libxkbcommon-dev:amd64 libxkbcommon-dev:i386 \ + libxkbregistry-dev:amd64 libxkbregistry-dev:i386 \ libxrandr-dev:amd64 libxrandr-dev:i386 \ libxrender-dev:amd64 libxrender-dev:i386 \ libxxf86vm-dev:amd64 libxxf86vm-dev:i386 \
From: Alexandros Frantzis alexandros.frantzis@collabora.com
Handle Wayland keyboard events and translate them to Windows events, currently using a hardcoded US key mapping. --- configure.ac | 5 +- dlls/winewayland.drv/Makefile.in | 5 +- dlls/winewayland.drv/wayland.c | 5 + dlls/winewayland.drv/wayland_keyboard.c | 245 ++++++++++++++++++++++++ dlls/winewayland.drv/wayland_surface.c | 3 + dlls/winewayland.drv/waylanddrv.h | 27 +++ include/config.h.in | 3 + 7 files changed, 290 insertions(+), 3 deletions(-) create mode 100644 dlls/winewayland.drv/wayland_keyboard.c
diff --git a/configure.ac b/configure.ac index 599d18eee43..0975f6fa565 100644 --- a/configure.ac +++ b/configure.ac @@ -1372,8 +1372,11 @@ then [AC_PATH_PROG(WAYLAND_SCANNER,wayland-scanner, [`test -n "$PKG_CONFIG" && $PKG_CONFIG --variable=wayland_scanner wayland-scanner 2>/dev/null`])], [WAYLAND_CLIENT_LIBS=""],[$WAYLAND_CLIENT_LIBS])])]) + WINE_PACKAGE_FLAGS(XKBCOMMON,[xkbcommon],,,, + [AC_CHECK_HEADERS([xkbcommon/xkbcommon.h]) + AC_CHECK_LIB(xkbcommon,xkb_context_new,[:],[XKBCOMMON_LIBS=""],[$XKBCOMMON_LIBS])]) fi -WINE_NOTICE_WITH(wayland, [test -z "$WAYLAND_CLIENT_LIBS" -o -z "$WAYLAND_SCANNER"], +WINE_NOTICE_WITH(wayland, [test -z "$WAYLAND_CLIENT_LIBS" -o -z "$WAYLAND_SCANNER" -o -z "$XKBCOMMON_LIBS"], [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 e1019ad8348..4fbc516266e 100644 --- a/dlls/winewayland.drv/Makefile.in +++ b/dlls/winewayland.drv/Makefile.in @@ -1,13 +1,14 @@ MODULE = winewayland.drv UNIXLIB = winewayland.so -UNIX_CFLAGS = $(WAYLAND_CLIENT_CFLAGS) -UNIX_LIBS = -lwin32u $(WAYLAND_CLIENT_LIBS) $(PTHREAD_LIBS) -lm +UNIX_CFLAGS = $(WAYLAND_CLIENT_CFLAGS) $(XKBCOMMON_CFLAGS) +UNIX_LIBS = -lwin32u $(WAYLAND_CLIENT_LIBS) $(XKBCOMMON_LIBS) $(PTHREAD_LIBS) -lm
SOURCES = \ display.c \ dllmain.c \ version.rc \ wayland.c \ + wayland_keyboard.c \ wayland_output.c \ wayland_pointer.c \ wayland_surface.c \ diff --git a/dlls/winewayland.drv/wayland.c b/dlls/winewayland.drv/wayland.c index b8c69a105cb..31c225e76d7 100644 --- a/dlls/winewayland.drv/wayland.c +++ b/dlls/winewayland.drv/wayland.c @@ -66,6 +66,11 @@ static void wl_seat_handle_capabilities(void *data, struct wl_seat *seat, wayland_pointer_init(wl_seat_get_pointer(seat)); else if (!(caps & WL_SEAT_CAPABILITY_POINTER) && process_wayland.pointer.wl_pointer) wayland_pointer_deinit(); + + if ((caps & WL_SEAT_CAPABILITY_KEYBOARD) && !process_wayland.keyboard.wl_keyboard) + wayland_keyboard_init(&process_wayland.keyboard, &process_wayland, wl_seat_get_keyboard(seat)); + else if (!(caps & WL_SEAT_CAPABILITY_KEYBOARD) && process_wayland.keyboard.wl_keyboard) + wayland_keyboard_deinit(&process_wayland.keyboard); }
static void wl_seat_handle_name(void *data, struct wl_seat *seat, const char *name) diff --git a/dlls/winewayland.drv/wayland_keyboard.c b/dlls/winewayland.drv/wayland_keyboard.c new file mode 100644 index 00000000000..837d307ec1e --- /dev/null +++ b/dlls/winewayland.drv/wayland_keyboard.c @@ -0,0 +1,245 @@ +/* + * Keyboard related functions + * + * Copyright 2020 Alexandros Frantzis for Collabora Ltd. + * Copyright 2023 Rémi Bernon for CodeWeavers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#if 0 +#pragma makedep unix +#endif + +#include "config.h" + +#include <linux/input.h> +#undef SW_MAX /* Also defined in winuser.rh */ +#include <unistd.h> + +#include "waylanddrv.h" +#include "wine/debug.h" + +WINE_DEFAULT_DEBUG_CHANNEL(keyboard); +WINE_DECLARE_DEBUG_CHANNEL(key); + +static WORD key2scan(UINT key) +{ + /* base keys can be mapped directly */ + if (key <= KEY_KPDOT) return key; + + /* map keys found in KBDTABLES definitions (Txx Xxx Yxx macros) */ + switch (key) + { + case 84 /* ISO_Level3_Shift */: return 0x005a; /* T5A / VK_OEM_WSCTRL */ + case KEY_SYSRQ: return 0x0054; /* T54 / VK_SNAPSHOT */ + case KEY_102ND: return 0x0056; /* T56 / VK_OEM_102 */ + case KEY_F11: return 0x0057; /* T57 / VK_F11 */ + case KEY_F12: return 0x0058; /* T58 / VK_F12 */ + case KEY_LINEFEED: return 0x0059; /* T59 / VK_CLEAR */ + case KEY_EXIT: return 0x005b; /* T5B / VK_OEM_FINISH */ + case KEY_OPEN: return 0x005c; /* T5C / VK_OEM_JUMP */ + /* case KEY_EREOF: return 0x005d; */ /* T5D / VK_EREOF */ + /* case KEY_OEM_BACKTAB: return 0x005e; */ /* T5E / VK_OEM_BACKTAB */ + case KEY_COMPOSE: return 0x005f; /* T5F / VK_OEM_AUTO */ + case KEY_SCALE: return 0x0062; /* T62 / VK_ZOOM */ + case KEY_HELP: return 0x0063; /* T63 / VK_HELP */ + case KEY_F13: return 0x0064; /* T64 / VK_F13 */ + case KEY_F14: return 0x0065; /* T65 / VK_F14 */ + case KEY_F15: return 0x0066; /* T66 / VK_F15 */ + case KEY_F16: return 0x0067; /* T67 / VK_F16 */ + case KEY_F17: return 0x0068; /* T68 / VK_F17 */ + case KEY_F18: return 0x0069; /* T69 / VK_F18 */ + case KEY_F19: return 0x006a; /* T6A / VK_F19 */ + case KEY_F20: return 0x006b; /* T6B / VK_F20 */ + case KEY_F21: return 0x006c; /* T6C / VK_F21 */ + case KEY_F22: return 0x006d; /* T6D / VK_F22 */ + case KEY_F23: return 0x006e; /* T6E / VK_F23 */ + /* case KEY_OEM_PA3: return 0x006f; */ /* T6F / VK_OEM_PA3 */ + case KEY_COMPUTER: return 0x0071; /* T71 / VK_OEM_RESET */ + /* case KEY_ABNT_C1: return 0x0073; */ /* T73 / VK_ABNT_C1 */ + case KEY_F24: return 0x0076; /* T76 / VK_F24 */ + case KEY_KPPLUSMINUS: return 0x007b; /* T7B / VK_OEM_PA1 */ + case KEY_TAB: return 0x007c; /* T7C / VK_TAB */ + /* case KEY_ABNT_C2: return 0x007e; */ /* T7E / VK_ABNT_C2 */ + /* case KEY_OEM_PA2: return 0x007f; */ /* T7F / VK_OEM_PA2 */ + case KEY_PREVIOUSSONG: return 0x0110; /* X10 / VK_MEDIA_PREV_TRACK */ + case KEY_NEXTSONG: return 0x0119; /* X19 / VK_MEDIA_NEXT_TRACK */ + case KEY_KPENTER: return 0x011c; /* X1C / VK_RETURN */ + case KEY_RIGHTCTRL: return 0x011d; /* X1D / VK_RCONTROL */ + case KEY_MUTE: return 0x0120; /* X20 / VK_VOLUME_MUTE */ + case KEY_PROG2: return 0x0121; /* X21 / VK_LAUNCH_APP2 */ + case KEY_PLAYPAUSE: return 0x0122; /* X22 / VK_MEDIA_PLAY_PAUSE */ + case KEY_STOPCD: return 0x0124; /* X24 / VK_MEDIA_STOP */ + case KEY_VOLUMEDOWN: return 0x012e; /* X2E / VK_VOLUME_DOWN */ + case KEY_VOLUMEUP: return 0x0130; /* X30 / VK_VOLUME_UP */ + case KEY_HOMEPAGE: return 0x0132; /* X32 / VK_BROWSER_HOME */ + case KEY_KPSLASH: return 0x0135; /* X35 / VK_DIVIDE */ + case KEY_PRINT: return 0x0137; /* X37 / VK_SNAPSHOT */ + case KEY_RIGHTALT: return 0x0138; /* X38 / VK_RMENU */ + case KEY_CANCEL: return 0x0146; /* X46 / VK_CANCEL */ + case KEY_HOME: return 0x0147; /* X47 / VK_HOME */ + case KEY_UP: return 0x0148; /* X48 / VK_UP */ + case KEY_PAGEUP: return 0x0149; /* X49 / VK_PRIOR */ + case KEY_LEFT: return 0x014b; /* X4B / VK_LEFT */ + case KEY_RIGHT: return 0x014d; /* X4D / VK_RIGHT */ + case KEY_END: return 0x014f; /* X4F / VK_END */ + case KEY_DOWN: return 0x0150; /* X50 / VK_DOWN */ + case KEY_PAGEDOWN: return 0x0151; /* X51 / VK_NEXT */ + case KEY_INSERT: return 0x0152; /* X52 / VK_INSERT */ + case KEY_DELETE: return 0x0153; /* X53 / VK_DELETE */ + case KEY_LEFTMETA: return 0x015b; /* X5B / VK_LWIN */ + case KEY_RIGHTMETA: return 0x015c; /* X5C / VK_RWIN */ + case KEY_MENU: return 0x015d; /* X5D / VK_APPS */ + case KEY_POWER: return 0x015e; /* X5E / VK_POWER */ + case KEY_SLEEP: return 0x015f; /* X5F / VK_SLEEP */ + case KEY_FIND: return 0x0165; /* X65 / VK_BROWSER_SEARCH */ + case KEY_BOOKMARKS: return 0x0166; /* X66 / VK_BROWSER_FAVORITES */ + case KEY_REFRESH: return 0x0167; /* X67 / VK_BROWSER_REFRESH */ + case KEY_STOP: return 0x0168; /* X68 / VK_BROWSER_STOP */ + case KEY_FORWARD: return 0x0169; /* X69 / VK_BROWSER_FORWARD */ + case KEY_BACK: return 0x016a; /* X6A / VK_BROWSER_BACK */ + case KEY_PROG1: return 0x016b; /* X6B / VK_LAUNCH_APP1 */ + case KEY_MAIL: return 0x016c; /* X6C / VK_LAUNCH_MAIL */ + case KEY_MEDIA: return 0x016d; /* X6D / VK_LAUNCH_MEDIA_SELECT */ + case KEY_PAUSE: return 0x021d; /* Y1D / VK_PAUSE */ + } + + /* otherwise just make up some extended scancode */ + return 0x200 | (key & 0x7f); +} + +/********************************************************************** + * Keyboard handling + */ + +static void keyboard_handle_keymap(void *data, struct wl_keyboard *wl_keyboard, + uint32_t format, int fd, uint32_t size) +{ + FIXME("stub!\n"); + close(fd); +} + +static void keyboard_handle_enter(void *data, struct wl_keyboard *wl_keyboard, + uint32_t serial, struct wl_surface *wl_surface, + struct wl_array *keys) +{ + struct wayland_keyboard *keyboard = &process_wayland.keyboard; + HWND hwnd; + + if (!wl_surface) return; + + /* The wl_surface user data remains valid and immutable for the whole + * lifetime of the object, so it's safe to access without locking. */ + hwnd = wl_surface_get_user_data(wl_surface); + TRACE("serial=%u hwnd=%p\n", serial, hwnd); + + keyboard->focused_hwnd = hwnd; + NtUserSetForegroundWindow(hwnd); +} + +static void keyboard_handle_leave(void *data, struct wl_keyboard *wl_keyboard, + uint32_t serial, struct wl_surface *wl_surface) +{ + struct wayland_keyboard *keyboard = &process_wayland.keyboard; + HWND hwnd; + + if (!wl_surface) return; + + /* The wl_surface user data remains valid and immutable for the whole + * lifetime of the object, so it's safe to access without locking. */ + hwnd = wl_surface_get_user_data(wl_surface); + TRACE("serial=%u hwnd=%p\n", serial, hwnd); + + if (keyboard->focused_hwnd == hwnd) + keyboard->focused_hwnd = NULL; + + /* FIXME: update foreground window as well */ +} + +static void keyboard_handle_key(void *data, struct wl_keyboard *wl_keyboard, + uint32_t serial, uint32_t time, uint32_t key, + uint32_t state) +{ + struct wayland_keyboard *keyboard = &process_wayland.keyboard; + UINT scan = key2scan(key); + INPUT input = {0}; + HWND hwnd; + + if (!(hwnd = keyboard->focused_hwnd)) return; + + TRACE_(key)("serial=%u hwnd=%p key=%d scan=%#x state=%#x\n", serial, hwnd, key, scan, state); + + input.type = INPUT_KEYBOARD; + input.ki.wScan = scan & 0xff; + input.ki.wVk = NtUserMapVirtualKeyEx(scan, MAPVK_VSC_TO_VK_EX, NtUserGetKeyboardLayout(0)); + if (scan & ~0xff) 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_modifiers(void *data, struct wl_keyboard *wl_keyboard, + uint32_t serial, uint32_t mods_depressed, + 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", + serial, mods_depressed, mods_latched, mods_locked, xkb_group); +} + +static void keyboard_handle_repeat_info(void *data, struct wl_keyboard *wl_keyboard, + int rate, int delay) +{ + FIXME("rate=%d delay=%d stub!\n", rate, delay); +} + +static const struct wl_keyboard_listener keyboard_listener = { + keyboard_handle_keymap, + keyboard_handle_enter, + keyboard_handle_leave, + keyboard_handle_key, + keyboard_handle_modifiers, + keyboard_handle_repeat_info, +}; + +/*********************************************************************** + * wayland_keyboard_init + */ +void wayland_keyboard_init(struct wayland_keyboard *keyboard, struct wayland *wayland, + struct wl_keyboard *wl_keyboard) +{ + keyboard->wl_keyboard = wl_keyboard; + + if (!(keyboard->xkb_context = xkb_context_new(XKB_CONTEXT_NO_FLAGS))) + { + ERR("Failed to create XKB context\n"); + return; + } + + wl_keyboard_add_listener(keyboard->wl_keyboard, &keyboard_listener, wayland); +} + +/*********************************************************************** + * wayland_keyboard_deinit + */ +void wayland_keyboard_deinit(struct wayland_keyboard *keyboard) +{ + if (keyboard->wl_keyboard) + wl_keyboard_destroy(keyboard->wl_keyboard); + + xkb_context_unref(keyboard->xkb_context); + memset(keyboard, 0, sizeof(*keyboard)); +} diff --git a/dlls/winewayland.drv/wayland_surface.c b/dlls/winewayland.drv/wayland_surface.c index ae4812ebb08..97d2d438489 100644 --- a/dlls/winewayland.drv/wayland_surface.c +++ b/dlls/winewayland.drv/wayland_surface.c @@ -181,6 +181,9 @@ void wayland_surface_destroy(struct wayland_surface *surface) } pthread_mutex_unlock(&process_wayland.pointer.mutex);
+ if (process_wayland.keyboard.focused_hwnd == surface->hwnd) + process_wayland.keyboard.focused_hwnd = NULL; + pthread_mutex_lock(&surface->mutex);
if (surface->xdg_toplevel) diff --git a/dlls/winewayland.drv/waylanddrv.h b/dlls/winewayland.drv/waylanddrv.h index 4bcd9e6706e..de2167ddc49 100644 --- a/dlls/winewayland.drv/waylanddrv.h +++ b/dlls/winewayland.drv/waylanddrv.h @@ -27,6 +27,7 @@
#include <pthread.h> #include <wayland-client.h> +#include <xkbcommon/xkbcommon.h> #include "xdg-output-unstable-v1-client-protocol.h" #include "xdg-shell-client-protocol.h"
@@ -65,6 +66,13 @@ enum wayland_surface_config_state WAYLAND_SURFACE_CONFIG_STATE_FULLSCREEN = (1 << 3) };
+struct wayland_keyboard +{ + struct wl_keyboard *wl_keyboard; + struct xkb_context *xkb_context; + HWND focused_hwnd; +}; + struct wayland_cursor { struct wayland_shm_buffer *shm_buffer; @@ -100,6 +108,7 @@ struct wayland struct xdg_wm_base *xdg_wm_base; struct wl_shm *wl_shm; struct wayland_seat seat; + struct wayland_keyboard keyboard; struct wayland_pointer pointer; struct wl_list output_list; /* Protects the output_list and the wayland_output.current states. */ @@ -223,6 +232,14 @@ void wayland_window_surface_update_wayland_surface(struct window_surface *surfac struct wayland_surface *wayland_surface) DECLSPEC_HIDDEN; void wayland_window_flush(HWND hwnd) DECLSPEC_HIDDEN;
+/********************************************************************** + * Wayland Keyboard + */ + +void wayland_keyboard_init(struct wayland_keyboard *keyboard, struct wayland *wayland, + struct wl_keyboard *wl_keyboard) DECLSPEC_HIDDEN; +void wayland_keyboard_deinit(struct wayland_keyboard *keyboard) DECLSPEC_HIDDEN; + /********************************************************************** * Wayland pointer */ @@ -248,6 +265,16 @@ static inline LRESULT send_message(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lp return NtUserMessageCall(hwnd, msg, wparam, lparam, NULL, NtUserSendMessage, FALSE); }
+static inline LRESULT send_message_timeout(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam, + UINT flags, UINT timeout, PDWORD_PTR res_ptr) +{ + struct send_message_timeout_params params = { .flags = flags, .timeout = timeout }; + LRESULT res = NtUserMessageCall(hwnd, msg, wparam, lparam, ¶ms, + NtUserSendMessageTimeout, FALSE); + if (res_ptr) *res_ptr = res; + return params.result; +} + RGNDATA *get_region_data(HRGN region) DECLSPEC_HIDDEN;
/********************************************************************** diff --git a/include/config.h.in b/include/config.h.in index fa234a82f84..0158be25799 100644 --- a/include/config.h.in +++ b/include/config.h.in @@ -675,6 +675,9 @@ /* Define to 1 if `callback' is a member of `XICCallback'. */ #undef HAVE_XICCALLBACK_CALLBACK
+/* Define to 1 if you have the <xkbcommon/xkbcommon.h> header file. */ +#undef HAVE_XKBCOMMON_XKBCOMMON_H + /* Define if Xrender has the XRenderCreateLinearGradient function */ #undef HAVE_XRENDERCREATELINEARGRADIENT
From: Rémi Bernon rbernon@codeweavers.com
--- dlls/win32u/message.c | 8 ++++++++ dlls/win32u/ntuser_private.h | 3 +++ dlls/win32u/sysparams.c | 3 +++ dlls/winewayland.drv/wayland_keyboard.c | 15 ++++++++++++++- include/ntuser.h | 1 + 5 files changed, 29 insertions(+), 1 deletion(-)
diff --git a/dlls/win32u/message.c b/dlls/win32u/message.c index d2909339983..e897ead329e 100644 --- a/dlls/win32u/message.c +++ b/dlls/win32u/message.c @@ -288,6 +288,7 @@ struct send_message_info };
static const INPUT_MESSAGE_SOURCE msg_source_unavailable = { IMDT_UNAVAILABLE, IMO_UNAVAILABLE }; +static BOOL keyboard_auto_repeat_enabled;
/* flag for messages that contain pointers */ /* 32 messages per entry, messages 0..31 map to bits 0..31 */ @@ -508,6 +509,13 @@ static inline BOOL check_hwnd_filter( const MSG *msg, HWND hwnd_filter ) return (msg->hwnd == hwnd_filter || is_child( hwnd_filter, msg->hwnd )); }
+BOOL set_keyboard_auto_repeat( BOOL enable ) +{ + BOOL enabled = keyboard_auto_repeat_enabled; + keyboard_auto_repeat_enabled = enable; + return enabled; +} + /*********************************************************************** * unpack_message * diff --git a/dlls/win32u/ntuser_private.h b/dlls/win32u/ntuser_private.h index b39e38db5d6..915aeb50bdc 100644 --- a/dlls/win32u/ntuser_private.h +++ b/dlls/win32u/ntuser_private.h @@ -242,6 +242,9 @@ HICON alloc_cursoricon_handle( BOOL is_icon ) DECLSPEC_HIDDEN; extern void free_dce( struct dce *dce, HWND hwnd ) DECLSPEC_HIDDEN; extern void invalidate_dce( WND *win, const RECT *extra_rect ) DECLSPEC_HIDDEN;
+/* message.c */ +extern BOOL set_keyboard_auto_repeat( BOOL enable ) DECLSPEC_HIDDEN; + /* window.c */ HANDLE alloc_user_handle( struct user_object *ptr, unsigned int type ) DECLSPEC_HIDDEN; void *free_user_handle( HANDLE handle, unsigned int type ) DECLSPEC_HIDDEN; diff --git a/dlls/win32u/sysparams.c b/dlls/win32u/sysparams.c index 5506abac7c6..57ee1f890f9 100644 --- a/dlls/win32u/sysparams.c +++ b/dlls/win32u/sysparams.c @@ -6315,6 +6315,9 @@ ULONG_PTR WINAPI NtUserCallOneParam( ULONG_PTR arg, ULONG code ) process_layout = arg; return TRUE;
+ case NtUserCallOneParam_SetKeyboardAutoRepeat: + return set_keyboard_auto_repeat( arg ); + /* temporary exports */ case NtUserGetDeskPattern: return get_entry( &entry_DESKPATTERN, 256, (WCHAR *)arg ); diff --git a/dlls/winewayland.drv/wayland_keyboard.c b/dlls/winewayland.drv/wayland_keyboard.c index 837d307ec1e..29ddb4591c4 100644 --- a/dlls/winewayland.drv/wayland_keyboard.c +++ b/dlls/winewayland.drv/wayland_keyboard.c @@ -203,7 +203,19 @@ static void keyboard_handle_modifiers(void *data, struct wl_keyboard *wl_keyboar static void keyboard_handle_repeat_info(void *data, struct wl_keyboard *wl_keyboard, int rate, int delay) { - FIXME("rate=%d delay=%d stub!\n", rate, delay); + UINT speed; + + TRACE("rate=%d delay=%d\n", rate, delay); + + /* Handle non-negative rate values, ignore invalid (negative) values. A + * rate of 0 disables repeat. */ + if (rate >= 80) speed = 31; + else if (rate >= 5) speed = rate * 400 / 1000 - 1; + else speed = 0; + + NtUserSystemParametersInfo(SPI_SETKEYBOARDSPEED, speed, NULL, 0); + NtUserSystemParametersInfo(SPI_SETKEYBOARDDELAY, max(0, min(3, delay / 250)), NULL, 0); + NtUserCallOneParam(rate > 0, NtUserCallOneParam_SetKeyboardAutoRepeat); }
static const struct wl_keyboard_listener keyboard_listener = { @@ -229,6 +241,7 @@ void wayland_keyboard_init(struct wayland_keyboard *keyboard, struct wayland *wa return; }
+ NtUserCallOneParam(TRUE, NtUserCallOneParam_SetKeyboardAutoRepeat); wl_keyboard_add_listener(keyboard->wl_keyboard, &keyboard_listener, wayland); }
diff --git a/include/ntuser.h b/include/ntuser.h index 171d32abe6e..ee2a677eabc 100644 --- a/include/ntuser.h +++ b/include/ntuser.h @@ -870,6 +870,7 @@ enum NtUserCallOneParam_ReplyMessage, NtUserCallOneParam_SetCaretBlinkTime, NtUserCallOneParam_SetProcessDefaultLayout, + NtUserCallOneParam_SetKeyboardAutoRepeat, /* temporary exports */ NtUserGetDeskPattern, };
From: Rémi Bernon rbernon@codeweavers.com
--- dlls/win32u/message.c | 46 ++++++++++++++++++++++++++++++++++++ dlls/win32u/ntuser_private.h | 4 ++++ 2 files changed, 50 insertions(+)
diff --git a/dlls/win32u/message.c b/dlls/win32u/message.c index e897ead329e..a8228335b36 100644 --- a/dlls/win32u/message.c +++ b/dlls/win32u/message.c @@ -2292,6 +2292,22 @@ static void send_parent_notify( HWND hwnd, WORD event, WORD idChild, POINT pt ) } }
+ +static void handle_keyboard_repeat_message( HWND hwnd ) +{ + struct user_thread_info *thread_info = get_user_thread_info(); + MSG *msg = &thread_info->key_repeat_msg; + UINT speed; + + msg->lParam = (msg->lParam & ~(LPARAM)0xffff) + ((msg->lParam + 1) & 0xffff); + + if (NtUserSystemParametersInfo( SPI_GETKEYBOARDSPEED, 0, &speed, 0 )) + NtUserSetSystemTimer( msg->hwnd, SYSTEM_TIMER_KEY_REPEAT, 400 / (speed + 1) ); + + NtUserPostMessage( hwnd, msg->message, msg->wParam, msg->lParam ); +} + + /*********************************************************************** * process_keyboard_message * @@ -2371,6 +2387,32 @@ static BOOL process_keyboard_message( MSG *msg, UINT hw_id, HWND hwnd_filter, if (ImmProcessKey( msg->hwnd, NtUserGetKeyboardLayout(0), msg->wParam, msg->lParam, 0 )) msg->wParam = VK_PROCESSKEY;
+ /* set/kill timers for key auto-repeat */ + if (remove && keyboard_auto_repeat_enabled) + { + switch (msg->message) + { + case WM_KEYDOWN: + case WM_SYSKEYDOWN: + { + struct user_thread_info *thread_info = get_user_thread_info(); + UINT delay; + + if (msg->wParam == VK_PROCESSKEY) break; + + thread_info->key_repeat_msg = *msg; + if (NtUserSystemParametersInfo( SPI_GETKEYBOARDDELAY, 0, &delay, 0 )) + NtUserSetSystemTimer( msg->hwnd, SYSTEM_TIMER_KEY_REPEAT, (delay + 1) * 250 ); + break; + } + + case WM_KEYUP: + case WM_SYSKEYUP: + kill_system_timer( msg->hwnd, SYSTEM_TIMER_KEY_REPEAT ); + break; + } + } + return TRUE; }
@@ -3565,6 +3607,10 @@ LRESULT WINAPI NtUserDispatchMessage( const MSG *msg ) case SYSTEM_TIMER_TRACK_MOUSE: update_mouse_tracking_info( msg->hwnd ); return 0; + + case SYSTEM_TIMER_KEY_REPEAT: + handle_keyboard_repeat_message( msg->hwnd ); + return 0; } }
diff --git a/dlls/win32u/ntuser_private.h b/dlls/win32u/ntuser_private.h index 915aeb50bdc..72a2f24bc49 100644 --- a/dlls/win32u/ntuser_private.h +++ b/dlls/win32u/ntuser_private.h @@ -32,6 +32,9 @@ enum system_timer_id { SYSTEM_TIMER_TRACK_MOUSE = 0xfffa, SYSTEM_TIMER_CARET = 0xffff, + + /* not compatible with native */ + SYSTEM_TIMER_KEY_REPEAT = 0xfff0, };
struct rawinput_thread_data @@ -126,6 +129,7 @@ struct user_thread_info struct received_message_info *receive_info; /* Message being currently received */ struct user_key_state_info *key_state; /* Cache of global key state */ struct imm_thread_data *imm_thread_data; /* IMM thread data */ + MSG key_repeat_msg; /* Last WM_KEYDOWN message to repeat */ HKL kbd_layout; /* Current keyboard layout */ UINT kbd_layout_id; /* Current keyboard layout ID */ struct rawinput_thread_data *rawinput; /* RawInput thread local data / buffer */
From: Rémi Bernon rbernon@codeweavers.com
--- dlls/winewayland.drv/wayland_keyboard.c | 46 +++++++++++++++++++++++-- dlls/winewayland.drv/waylanddrv.h | 1 + 2 files changed, 45 insertions(+), 2 deletions(-)
diff --git a/dlls/winewayland.drv/wayland_keyboard.c b/dlls/winewayland.drv/wayland_keyboard.c index 29ddb4591c4..5566141e1cb 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" @@ -128,8 +129,39 @@ static WORD key2scan(UINT key) 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) + xkb_keymap = xkb_keymap_new_from_buffer(keyboard->xkb_context, keymap_str, size, + XKB_KEYMAP_FORMAT_TEXT_V1, 0); + else + FIXME("Unsupported keymap format %#x\n", format); + + 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))) + { + xkb_state_unref(keyboard->xkb_state); + keyboard->xkb_state = xkb_state; + } + + xkb_keymap_unref(xkb_keymap); }
static void keyboard_handle_enter(void *data, struct wl_keyboard *wl_keyboard, @@ -196,8 +228,17 @@ 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 (!keyboard->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); + + xkb_state_update_mask(keyboard->xkb_state, mods_depressed, mods_latched, + mods_locked, 0, 0, xkb_group); + + /* TODO: Sync wine modifier state with XKB modifier state. */ }
static void keyboard_handle_repeat_info(void *data, struct wl_keyboard *wl_keyboard, @@ -253,6 +294,7 @@ void wayland_keyboard_deinit(struct wayland_keyboard *keyboard) if (keyboard->wl_keyboard) wl_keyboard_destroy(keyboard->wl_keyboard);
+ xkb_state_unref(keyboard->xkb_state); xkb_context_unref(keyboard->xkb_context); memset(keyboard, 0, sizeof(*keyboard)); } diff --git a/dlls/winewayland.drv/waylanddrv.h b/dlls/winewayland.drv/waylanddrv.h index de2167ddc49..2fdb9fe679e 100644 --- a/dlls/winewayland.drv/waylanddrv.h +++ b/dlls/winewayland.drv/waylanddrv.h @@ -70,6 +70,7 @@ struct wayland_keyboard { struct wl_keyboard *wl_keyboard; struct xkb_context *xkb_context; + struct xkb_state *xkb_state; HWND focused_hwnd; };
From: Rémi Bernon rbernon@codeweavers.com
--- configure.ac | 5 +- dlls/winewayland.drv/Makefile.in | 4 +- dlls/winewayland.drv/wayland_keyboard.c | 245 +++++++++++++++++++++++- dlls/winewayland.drv/waylanddrv.h | 2 + 4 files changed, 251 insertions(+), 5 deletions(-)
diff --git a/configure.ac b/configure.ac index 0975f6fa565..8d4d9d4f4cb 100644 --- a/configure.ac +++ b/configure.ac @@ -1375,8 +1375,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"], +WINE_NOTICE_WITH(wayland, [test -z "$WAYLAND_CLIENT_LIBS" -o -z "$WAYLAND_SCANNER" -o -z "$XKBCOMMON_LIBS" -o -z "$XKBREGISTRY_LIBS"], [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 4fbc516266e..e096a422806 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 5566141e1cb..415357cb30c 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,21 @@ 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; +}; + +static struct list xkb_layouts = LIST_INIT(xkb_layouts); +static struct rxkb_context *rxkb_context; + static WORD key2scan(UINT key) { /* base keys can be mapped directly */ @@ -122,6 +138,194 @@ 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 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); + ptr += len; + + layout->xkb_group = xkb_group; + layout->lang = lang; + layout->index = index; + if (index) layout->layout_id = next_layout_id++; + + TRACE("Created layout entry %p, hkl %04x%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)); + HKL hkl = keyboard->last_hkl; + struct layout *layout; + + LIST_FOR_EACH_ENTRY(layout, &xkb_layouts, struct layout, entry) + if (layout->xkb_group == xkb_group) break; + if (&layout->entry == &xkb_layouts) + ERR("Failed to find Xkb Layout for group %d\n", xkb_group); + else + { + if (!layout->layout_id) hkl = (HKL)(UINT_PTR)MAKELONG(locale, layout->lang); + else hkl = (HKL)(UINT_PTR)MAKELONG(locale, 0xf000 | layout->layout_id); + } + + if (hkl == keyboard->last_hkl) return; + keyboard->last_hkl = hkl; + + TRACE("Changing keyboard layout to %p\n", hkl); + NtUserPostMessage(keyboard->focused_hwnd, WM_INPUTLANGCHANGEREQUEST, 0 /*FIXME*/, + (LPARAM)keyboard->last_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 */ @@ -131,7 +335,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); @@ -139,7 +345,7 @@ static void keyboard_handle_keymap(void *data, struct wl_keyboard *wl_keyboard, if ((keymap_str = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0)) != MAP_FAILED) { if (format == WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1) - xkb_keymap = xkb_keymap_new_from_buffer(keyboard->xkb_context, keymap_str, size, + xkb_keymap = xkb_keymap_new_from_string(keyboard->xkb_context, keymap_str, XKB_KEYMAP_FORMAT_TEXT_V1, 0); else FIXME("Unsupported keymap format %#x\n", format); @@ -155,6 +361,31 @@ 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))) { xkb_state_unref(keyboard->xkb_state); @@ -162,6 +393,8 @@ static void keyboard_handle_keymap(void *data, struct wl_keyboard *wl_keyboard, }
xkb_keymap_unref(xkb_keymap); + + set_current_xkb_group(0); }
static void keyboard_handle_enter(void *data, struct wl_keyboard *wl_keyboard, @@ -180,6 +413,9 @@ static void keyboard_handle_enter(void *data, struct wl_keyboard *wl_keyboard,
keyboard->focused_hwnd = hwnd; NtUserSetForegroundWindow(hwnd); + + NtUserPostMessage(keyboard->focused_hwnd, WM_INPUTLANGCHANGEREQUEST, 0 /*FIXME*/, + (LPARAM)keyboard->last_hkl); }
static void keyboard_handle_leave(void *data, struct wl_keyboard *wl_keyboard, @@ -216,7 +452,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->last_hkl); if (scan & ~0xff) input.ki.dwFlags |= KEYEVENTF_EXTENDEDKEY;
if (state == WL_KEYBOARD_KEY_STATE_RELEASED) input.ki.dwFlags |= KEYEVENTF_KEYUP; @@ -237,6 +473,7 @@ static void keyboard_handle_modifiers(void *data, struct wl_keyboard *wl_keyboar
xkb_state_update_mask(keyboard->xkb_state, mods_depressed, mods_latched, mods_locked, 0, 0, xkb_group); + set_current_xkb_group(xkb_group);
/* TODO: Sync wine modifier state with XKB modifier state. */ } @@ -282,6 +519,10 @@ void wayland_keyboard_init(struct wayland_keyboard *keyboard, struct wayland *wa return; }
+ rxkb_context = rxkb_context_new(RXKB_CONTEXT_NO_FLAGS); + if (!rxkb_context_parse_default_ruleset(rxkb_context)) + ERR("Failed to parse default Xkb ruleset\n"); + NtUserCallOneParam(TRUE, NtUserCallOneParam_SetKeyboardAutoRepeat); wl_keyboard_add_listener(keyboard->wl_keyboard, &keyboard_listener, wayland); } diff --git a/dlls/winewayland.drv/waylanddrv.h b/dlls/winewayland.drv/waylanddrv.h index 2fdb9fe679e..801cfd0e3ef 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 "xdg-output-unstable-v1-client-protocol.h" #include "xdg-shell-client-protocol.h"
@@ -72,6 +73,7 @@ struct wayland_keyboard struct xkb_context *xkb_context; struct xkb_state *xkb_state; HWND focused_hwnd; + HKL last_hkl; };
struct wayland_cursor
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 4c48f2ca9d0..f718b274176 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 c4e859f21e5..dcaceee8b48 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
--- 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 3e6e440de93..4c48f2ca9d0 100644 --- a/dlls/win32u/input.c +++ b/dlls/win32u/input.c @@ -1141,7 +1141,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
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/winewayland.drv/wayland_keyboard.c | 301 +++++++++++++++++++++++- dlls/winewayland.drv/waylanddrv.h | 2 + dlls/winewayland.drv/waylanddrv_main.c | 2 + 3 files changed, 299 insertions(+), 6 deletions(-)
diff --git a/dlls/winewayland.drv/wayland_keyboard.c b/dlls/winewayland.drv/wayland_keyboard.c index 415357cb30c..fb2f4b0ab6f 100644 --- a/dlls/winewayland.drv/wayland_keyboard.c +++ b/dlls/winewayland.drv/wayland_keyboard.c @@ -41,14 +41,96 @@ 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])]; + }; +}; + +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 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 pthread_mutex_t xkb_layouts_mutex = PTHREAD_MUTEX_INITIALIZER; static struct list xkb_layouts = LIST_INIT(xkb_layouts); static struct rxkb_context *rxkb_context;
@@ -250,27 +332,47 @@ static inline LANGID langid_from_xkb_layout(const char *layout, size_t layout_le return MAKELANGID(LANG_NEUTRAL, SUBLANG_CUSTOM_UNSPECIFIED); };
-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;
layout->xkb_layout = strcpy(ptr, xkb_layout); ptr += len; @@ -279,6 +381,130 @@ static void add_xkb_layout(const char *xkb_layout, xkb_layout_index_t xkb_group, layout->lang = lang; layout->index = index; if (index) layout->layout_id = next_layout_id++; + layout->key_names_str = names_str = (void *)ptr; + + 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; + } + + 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, entry->vsc, 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, .Attributes = CAPLOK}; + BOOL found = FALSE; + unsigned int mod; + + for (mod = 0; mod < 8; ++mod) + { + xkb_mod_mask_t mod_mask = 0; + uint32_t ret = 0; + + 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 (ret) 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, hkl %04x%04x id %04x\n", layout, layout->index, layout->lang, layout->layout_id); list_add_tail(&xkb_layouts, &layout->entry); @@ -291,6 +517,8 @@ static void set_current_xkb_group(xkb_layout_index_t xkb_group) HKL hkl = keyboard->last_hkl; struct layout *layout;
+ 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) @@ -301,6 +529,8 @@ static void set_current_xkb_group(xkb_layout_index_t xkb_group) else hkl = (HKL)(UINT_PTR)MAKELONG(locale, 0xf000 | layout->layout_id); }
+ pthread_mutex_unlock(&xkb_layouts_mutex); + if (hkl == keyboard->last_hkl) return; keyboard->last_hkl = hkl;
@@ -361,10 +591,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++) @@ -383,9 +615,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))) { xkb_state_unref(keyboard->xkb_state); @@ -437,6 +671,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) @@ -450,6 +695,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->last_hkl); @@ -539,3 +787,44 @@ void wayland_keyboard_deinit(struct wayland_keyboard *keyboard) xkb_context_unref(keyboard->xkb_context); memset(keyboard, 0, sizeof(*keyboard)); } + +/*********************************************************************** + * 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 (!layout->layout_id && layout->lang == HIWORD(hkl)) break; + if (layout->layout_id && (layout->layout_id | 0xf000) == HIWORD(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 801cfd0e3ef..fa5e12acf77 100644 --- a/dlls/winewayland.drv/waylanddrv.h +++ b/dlls/winewayland.drv/waylanddrv.h @@ -242,6 +242,8 @@ void wayland_window_flush(HWND hwnd) DECLSPEC_HIDDEN; void wayland_keyboard_init(struct wayland_keyboard *keyboard, struct wayland *wayland, struct wl_keyboard *wl_keyboard) DECLSPEC_HIDDEN; void wayland_keyboard_deinit(struct wayland_keyboard *keyboard) DECLSPEC_HIDDEN; +const KBDTABLES *WAYLAND_KbdLayerDescriptor(HKL hkl) DECLSPEC_HIDDEN; +void WAYLAND_ReleaseKbdTables(const KBDTABLES *) DECLSPEC_HIDDEN;
/********************************************************************** * Wayland pointer diff --git a/dlls/winewayland.drv/waylanddrv_main.c b/dlls/winewayland.drv/waylanddrv_main.c index 7151d7b931a..93d32e9e38a 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.c | 1 + dlls/winewayland.drv/wayland_keyboard.c | 231 ++++++++++++++++++++++++ dlls/winewayland.drv/waylanddrv.h | 8 +- dlls/winewayland.drv/waylanddrv_main.c | 2 + 4 files changed, 241 insertions(+), 1 deletion(-)
diff --git a/dlls/winewayland.drv/wayland.c b/dlls/winewayland.drv/wayland.c index 31c225e76d7..0740d4c179a 100644 --- a/dlls/winewayland.drv/wayland.c +++ b/dlls/winewayland.drv/wayland.c @@ -35,6 +35,7 @@ WINE_DEFAULT_DEBUG_CHANNEL(waylanddrv); struct wayland process_wayland = { .seat.mutex = PTHREAD_MUTEX_INITIALIZER, + .keyboard.mutex = PTHREAD_MUTEX_INITIALIZER, .pointer.mutex = PTHREAD_MUTEX_INITIALIZER, .output_list = {&process_wayland.output_list, &process_wayland.output_list}, .output_mutex = PTHREAD_MUTEX_INITIALIZER diff --git a/dlls/winewayland.drv/wayland_keyboard.c b/dlls/winewayland.drv/wayland_keyboard.c index fb2f4b0ab6f..ac0b5128323 100644 --- a/dlls/winewayland.drv/wayland_keyboard.c +++ b/dlls/winewayland.drv/wayland_keyboard.c @@ -31,8 +31,11 @@ #include <sys/mman.h> #include <unistd.h>
+#include "ntstatus.h" +#define WIN32_NO_STATUS #include "waylanddrv.h" #include "wine/debug.h" +#include "ime.h"
WINE_DEFAULT_DEBUG_CHANNEL(keyboard); WINE_DECLARE_DEBUG_CHANNEL(key); @@ -220,6 +223,91 @@ static WORD key2scan(UINT key) return 0x200 | (key & 0x7f); }
+static UINT scan2key(WORD scan) +{ + /* base keys can be mapped directly */ + if (scan <= KEY_KPDOT) return scan; + + /* map keys found in KBDTABLES definitions (Txx Xxx Yxx macros) */ + switch (scan) + { + case 0x005a: return 84 /* ISO_Level3_Shift */; /* T5A / VK_OEM_WSCTRL */ + case 0x0054: return KEY_SYSRQ; /* T54 / VK_SNAPSHOT */ + case 0x0056: return KEY_102ND; /* T56 / VK_OEM_102 */ + case 0x0057: return KEY_F11; /* T57 / VK_F11 */ + case 0x0058: return KEY_F12; /* T58 / VK_F12 */ + case 0x0059: return KEY_LINEFEED; /* T59 / VK_CLEAR */ + case 0x005b: return KEY_EXIT; /* T5B / VK_OEM_FINISH */ + case 0x005c: return KEY_OPEN; /* T5C / VK_OEM_JUMP */ + /* case 0x005d: return KEY_EREOF; */ /* T5D / VK_EREOF */ + /* case 0x005e: return KEY_OEM_BACKTAB; */ /* T5E / VK_OEM_BACKTAB */ + case 0x005f: return KEY_COMPOSE; /* T5F / VK_OEM_AUTO */ + case 0x0062: return KEY_SCALE; /* T62 / VK_ZOOM */ + case 0x0063: return KEY_HELP; /* T63 / VK_HELP */ + case 0x0064: return KEY_F13; /* T64 / VK_F13 */ + case 0x0065: return KEY_F14; /* T65 / VK_F14 */ + case 0x0066: return KEY_F15; /* T66 / VK_F15 */ + case 0x0067: return KEY_F16; /* T67 / VK_F16 */ + case 0x0068: return KEY_F17; /* T68 / VK_F17 */ + case 0x0069: return KEY_F18; /* T69 / VK_F18 */ + case 0x006a: return KEY_F19; /* T6A / VK_F19 */ + case 0x006b: return KEY_F20; /* T6B / VK_F20 */ + case 0x006c: return KEY_F21; /* T6C / VK_F21 */ + case 0x006d: return KEY_F22; /* T6D / VK_F22 */ + case 0x006e: return KEY_F23; /* T6E / VK_F23 */ + /* case 0x006f: return KEY_OEM_PA3; */ /* T6F / VK_OEM_PA3 */ + case 0x0071: return KEY_COMPUTER; /* T71 / VK_OEM_RESET */ + /* case 0x0073: return KEY_ABNT_C1; */ /* T73 / VK_ABNT_C1 */ + case 0x0076: return KEY_F24; /* T76 / VK_F24 */ + case 0x007b: return KEY_KPPLUSMINUS; /* T7B / VK_OEM_PA1 */ + case 0x007c: return KEY_TAB; /* T7C / VK_TAB */ + /* case 0x007e: return KEY_ABNT_C2; */ /* T7E / VK_ABNT_C2 */ + /* case 0x007f: return KEY_OEM_PA2; */ /* T7F / VK_OEM_PA2 */ + case 0x0110: return KEY_PREVIOUSSONG; /* X10 / VK_MEDIA_PREV_TRACK */ + case 0x0119: return KEY_NEXTSONG; /* X19 / VK_MEDIA_NEXT_TRACK */ + case 0x011c: return KEY_KPENTER; /* X1C / VK_RETURN */ + case 0x011d: return KEY_RIGHTCTRL; /* X1D / VK_RCONTROL */ + case 0x0120: return KEY_MUTE; /* X20 / VK_VOLUME_MUTE */ + case 0x0121: return KEY_PROG2; /* X21 / VK_LAUNCH_APP2 */ + case 0x0122: return KEY_PLAYPAUSE; /* X22 / VK_MEDIA_PLAY_PAUSE */ + case 0x0124: return KEY_STOPCD; /* X24 / VK_MEDIA_STOP */ + case 0x012e: return KEY_VOLUMEDOWN; /* X2E / VK_VOLUME_DOWN */ + case 0x0130: return KEY_VOLUMEUP; /* X30 / VK_VOLUME_UP */ + case 0x0132: return KEY_HOMEPAGE; /* X32 / VK_BROWSER_HOME */ + case 0x0135: return KEY_KPSLASH; /* X35 / VK_DIVIDE */ + case 0x0137: return KEY_PRINT; /* X37 / VK_SNAPSHOT */ + case 0x0138: return KEY_RIGHTALT; /* X38 / VK_RMENU */ + case 0x0146: return KEY_CANCEL; /* X46 / VK_CANCEL */ + case 0x0147: return KEY_HOME; /* X47 / VK_HOME */ + case 0x0148: return KEY_UP; /* X48 / VK_UP */ + case 0x0149: return KEY_PAGEUP; /* X49 / VK_PRIOR */ + case 0x014b: return KEY_LEFT; /* X4B / VK_LEFT */ + case 0x014d: return KEY_RIGHT; /* X4D / VK_RIGHT */ + case 0x014f: return KEY_END; /* X4F / VK_END */ + case 0x0150: return KEY_DOWN; /* X50 / VK_DOWN */ + case 0x0151: return KEY_PAGEDOWN; /* X51 / VK_NEXT */ + case 0x0152: return KEY_INSERT; /* X52 / VK_INSERT */ + case 0x0153: return KEY_DELETE; /* X53 / VK_DELETE */ + case 0x015b: return KEY_LEFTMETA; /* X5B / VK_LWIN */ + case 0x015c: return KEY_RIGHTMETA; /* X5C / VK_RWIN */ + case 0x015d: return KEY_MENU; /* X5D / VK_APPS */ + case 0x015e: return KEY_POWER; /* X5E / VK_POWER */ + case 0x015f: return KEY_SLEEP; /* X5F / VK_SLEEP */ + case 0x0165: return KEY_FIND; /* X65 / VK_BROWSER_SEARCH */ + case 0x0166: return KEY_BOOKMARKS; /* X66 / VK_BROWSER_FAVORITES */ + case 0x0167: return KEY_REFRESH; /* X67 / VK_BROWSER_REFRESH */ + case 0x0168: return KEY_STOP; /* X68 / VK_BROWSER_STOP */ + case 0x0169: return KEY_FORWARD; /* X69 / VK_BROWSER_FORWARD */ + case 0x016a: return KEY_BACK; /* X6A / VK_BROWSER_BACK */ + case 0x016b: return KEY_PROG1; /* X6B / VK_LAUNCH_APP1 */ + case 0x016c: return KEY_MAIL; /* X6C / VK_LAUNCH_MAIL */ + case 0x016d: return KEY_MEDIA; /* X6D / VK_LAUNCH_MEDIA_SELECT */ + case 0x021d: return KEY_PAUSE; /* Y1D / VK_PAUSE */ + } + + return 0; +} + static inline LANGID langid_from_xkb_layout(const char *layout, size_t layout_len) { #define MAKEINDEX(c0, c1) (MAKEWORD(c0, c1) - MAKEWORD('a', 'a')) @@ -622,8 +710,10 @@ static void keyboard_handle_keymap(void *data, struct wl_keyboard *wl_keyboard,
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); @@ -719,8 +809,11 @@ static void keyboard_handle_modifiers(void *data, struct wl_keyboard *wl_keyboar 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); + set_current_xkb_group(xkb_group);
/* TODO: Sync wine modifier state with XKB modifier state. */ @@ -759,6 +852,14 @@ static const struct wl_keyboard_listener keyboard_listener = { void wayland_keyboard_init(struct wayland_keyboard *keyboard, struct wayland *wayland, struct wl_keyboard *wl_keyboard) { + struct xkb_compose_table *compose_table; + const char *locale; + + locale = getenv("LC_ALL"); + if (!locale || !*locale) locale = getenv("LC_CTYPE"); + if (!locale || !*locale) locale = getenv("LANG"); + if (!locale || !*locale) locale = "C"; + keyboard->wl_keyboard = wl_keyboard;
if (!(keyboard->xkb_context = xkb_context_new(XKB_CONTEXT_NO_FLAGS))) @@ -767,6 +868,17 @@ void wayland_keyboard_init(struct wayland_keyboard *keyboard, struct wayland *wa return; }
+ if ((compose_table = xkb_compose_table_new_from_locale(keyboard->xkb_context, locale, + XKB_COMPOSE_COMPILE_NO_FLAGS))) + { + keyboard->xkb_compose_state = + xkb_compose_state_new(compose_table, XKB_COMPOSE_STATE_NO_FLAGS); + xkb_compose_table_unref(compose_table); + } + + if (!keyboard->xkb_compose_state) + ERR("Failed to create XKB compose table\n"); + rxkb_context = rxkb_context_new(RXKB_CONTEXT_NO_FLAGS); if (!rxkb_context_parse_default_ruleset(rxkb_context)) ERR("Failed to parse default Xkb ruleset\n"); @@ -783,6 +895,7 @@ void wayland_keyboard_deinit(struct wayland_keyboard *keyboard) if (keyboard->wl_keyboard) wl_keyboard_destroy(keyboard->wl_keyboard);
+ xkb_compose_state_unref(keyboard->xkb_compose_state); xkb_state_unref(keyboard->xkb_state); xkb_context_unref(keyboard->xkb_context); memset(keyboard, 0, sizeof(*keyboard)); @@ -828,3 +941,121 @@ void WAYLAND_ReleaseKbdTables(const KBDTABLES *tables) struct layout *layout = CONTAINING_RECORD(tables, struct layout, tables); xkb_layout_release(layout); } + +/*********************************************************************** + * ImeProcessKey (WAYLANDDRV.@) + */ +UINT WAYLAND_ImeProcessKey(HIMC himc, UINT wparam, UINT lparam, const BYTE *key_state) +{ + struct wayland_keyboard *keyboard = &process_wayland.keyboard; + WORD scan = HIWORD(lparam) & 0x1ff, vkey = LOWORD(wparam); + BOOL repeat = !!(lparam >> 30), pressed = !(lparam >> 31); + UINT key = scan2key(scan); + xkb_keysym_t xkb_keysym; + BOOL ret = FALSE; + + TRACE_(key)("himc=%p scan=%#x vkey=%#x repeat=%u pressed=%u\n", + himc, scan, vkey, repeat, pressed); + + if (!pressed) return FALSE; + + pthread_mutex_lock(&keyboard->mutex); + + if ((xkb_keysym = xkb_state_key_get_one_sym(keyboard->xkb_state, key + 8)) != XKB_KEY_NoSymbol && + (xkb_compose_state_feed(keyboard->xkb_compose_state, xkb_keysym)) == XKB_COMPOSE_FEED_ACCEPTED) + { + enum xkb_compose_status compose_status = xkb_compose_state_get_status(keyboard->xkb_compose_state); + if (compose_status == XKB_COMPOSE_COMPOSING || compose_status == XKB_COMPOSE_COMPOSED) ret = TRUE; + } + + pthread_mutex_unlock(&keyboard->mutex); + + return ret; +} + +/*********************************************************************** + * ImeToAsciiEx (WAYLANDDRV.@) + */ +UINT WAYLAND_ImeToAsciiEx(UINT vkey, UINT vsc, const BYTE *state, COMPOSITIONSTRING *compstr, HIMC himc) +{ + DWORD needed = sizeof(COMPOSITIONSTRING), comp_len, result_len, len, size; + struct wayland_keyboard *keyboard = &process_wayland.keyboard; + enum xkb_compose_status compose_status; + char buffer[64]; + void *dst; + + TRACE_(key)("vkey=%#x vsc=%#x state=%p compstr=%p himc=%p\n", vkey, vsc, state, compstr, himc); + + pthread_mutex_lock(&keyboard->mutex); + compose_status = xkb_compose_state_get_status(keyboard->xkb_compose_state); + len = xkb_compose_state_get_utf8(keyboard->xkb_compose_state, buffer, sizeof(buffer)); + pthread_mutex_unlock(&keyboard->mutex); + + if (compose_status != XKB_COMPOSE_COMPOSING) comp_len = 0; + else + { + comp_len = len; + needed += comp_len * sizeof(WCHAR); /* GCS_COMPSTR */ + needed += comp_len; /* GCS_COMPATTR */ + needed += 2 * sizeof(DWORD); /* GCS_COMPCLAUSE */ + } + + if (compose_status != XKB_COMPOSE_COMPOSED) result_len = 0; + else + { + result_len = len; + needed += result_len * sizeof(WCHAR); /* GCS_RESULTSTR */ + needed += 2 * sizeof(DWORD); /* GCS_RESULTCLAUSE */ + } + + if (compstr->dwSize < needed) + { + compstr->dwSize = needed; + return STATUS_BUFFER_TOO_SMALL; + } + + memset(compstr, 0, sizeof(*compstr)); + compstr->dwSize = sizeof(*compstr); + + if (compose_status == XKB_COMPOSE_COMPOSING) + { + compstr->dwCursorPos = 0; + + compstr->dwCompStrOffset = compstr->dwSize; + dst = (BYTE *)compstr + compstr->dwCompStrOffset; + RtlUTF8ToUnicodeN(dst, len * sizeof(WCHAR), &size, buffer, len); + compstr->dwCompStrLen = size / sizeof(WCHAR); + compstr->dwSize += size; + + compstr->dwCompClauseLen = 2 * sizeof(DWORD); + compstr->dwCompClauseOffset = compstr->dwSize; + dst = (BYTE *)compstr + compstr->dwCompClauseOffset; + *((DWORD *)dst + 0) = 0; + *((DWORD *)dst + 1) = compstr->dwCompStrLen; + compstr->dwSize += compstr->dwCompClauseLen; + + compstr->dwCompAttrLen = compstr->dwCompStrLen; + compstr->dwCompAttrOffset = compstr->dwSize; + dst = (BYTE *)compstr + compstr->dwCompAttrOffset; + memset(dst, ATTR_INPUT, compstr->dwCompAttrLen); + compstr->dwSize += compstr->dwCompAttrLen; + } + + if (compose_status == XKB_COMPOSE_COMPOSED) + { + compstr->dwResultStrOffset = compstr->dwSize; + dst = (BYTE *)compstr + compstr->dwResultStrOffset; + RtlUTF8ToUnicodeN(dst, len * sizeof(WCHAR), &size, buffer, len); + compstr->dwResultStrLen = size / sizeof(WCHAR); + compstr->dwSize += size; + + compstr->dwResultClauseLen = 2 * sizeof(DWORD); + compstr->dwResultClauseOffset = compstr->dwSize; + dst = (BYTE *)compstr + compstr->dwResultClauseOffset; + *((DWORD *)dst + 0) = 0; + *((DWORD *)dst + 1) = compstr->dwResultStrLen; + compstr->dwSize += compstr->dwResultClauseLen; + } + + return 0; +} diff --git a/dlls/winewayland.drv/waylanddrv.h b/dlls/winewayland.drv/waylanddrv.h index fa5e12acf77..52d56280d78 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/xkbcommon-compose.h> #include <xkbcommon/xkbregistry.h> #include "xdg-output-unstable-v1-client-protocol.h" #include "xdg-shell-client-protocol.h" @@ -71,9 +72,12 @@ struct wayland_keyboard { struct wl_keyboard *wl_keyboard; struct xkb_context *xkb_context; - struct xkb_state *xkb_state; HWND focused_hwnd; HKL last_hkl; + + pthread_mutex_t mutex; + struct xkb_state *xkb_state; + struct xkb_compose_state *xkb_compose_state; };
struct wayland_cursor @@ -244,6 +248,8 @@ void wayland_keyboard_init(struct wayland_keyboard *keyboard, struct wayland *wa void wayland_keyboard_deinit(struct wayland_keyboard *keyboard) DECLSPEC_HIDDEN; const KBDTABLES *WAYLAND_KbdLayerDescriptor(HKL hkl) DECLSPEC_HIDDEN; void WAYLAND_ReleaseKbdTables(const KBDTABLES *) DECLSPEC_HIDDEN; +UINT WAYLAND_ImeProcessKey(HIMC himc, UINT wparam, UINT lparam, const BYTE *key_state) DECLSPEC_HIDDEN; +UINT WAYLAND_ImeToAsciiEx(UINT vkey, UINT vsc, const BYTE *state, COMPOSITIONSTRING *compstr, HIMC himc) DECLSPEC_HIDDEN;
/********************************************************************** * Wayland pointer diff --git a/dlls/winewayland.drv/waylanddrv_main.c b/dlls/winewayland.drv/waylanddrv_main.c index 93d32e9e38a..89a32edf9e6 100644 --- a/dlls/winewayland.drv/waylanddrv_main.c +++ b/dlls/winewayland.drv/waylanddrv_main.c @@ -35,6 +35,8 @@ static const struct user_driver_funcs waylanddrv_funcs = .pDestroyWindow = WAYLAND_DestroyWindow, .pKbdLayerDescriptor = WAYLAND_KbdLayerDescriptor, .pReleaseKbdTables = WAYLAND_ReleaseKbdTables, + .pImeProcessKey = WAYLAND_ImeProcessKey, + .pImeToAsciiEx = WAYLAND_ImeToAsciiEx, .pSetCursor = WAYLAND_SetCursor, .pSysCommand = WAYLAND_SysCommand, .pUpdateDisplayDevices = WAYLAND_UpdateDisplayDevices,
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;
sadly this doesn't build for me I doubt I'm missing deps [build.txt](/uploads/d82e9feb258a4047618afb8ebbff6cc9/build.txt)
I found an MR talking about `linux/input.h` being missing under some conditions: https://gitlab.winehq.org/wine/wine/-/merge_requests/4132
On Wed Oct 18 12:03:56 2023 +0000, Vixea wrote:
sadly this doesn't build for me I doubt I'm missing deps [build.txt](/uploads/d82e9feb258a4047618afb8ebbff6cc9/build.txt)
You need to run `autoreconf -fi` to generate the `configure` changes, they aren't included in the commits.
On Wed Oct 18 12:03:56 2023 +0000, Rémi Bernon wrote:
You need to run `autoreconf -fi` to generate the `configure` changes, they aren't included in the commits.
yea it builds now
Tab key enters infinite loop.
![Screencast_from_2023-10-20_23-00-49](/uploads/9a54bb9b70cfc241a211ec66f34e26bb/Screencast_from_2023-10-20_23-00-49.webm)