-- v3: 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: Rémi Bernon rbernon@codeweavers.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 | 6 + dlls/winewayland.drv/wayland_keyboard.c | 274 ++++++++++++++++++++++++ dlls/winewayland.drv/wayland_surface.c | 5 + dlls/winewayland.drv/waylanddrv.h | 17 ++ 6 files changed, 309 insertions(+), 3 deletions(-) create mode 100644 dlls/winewayland.drv/wayland_keyboard.c
diff --git a/configure.ac b/configure.ac index fd1a58b9170..1854c6f0460 100644 --- a/configure.ac +++ b/configure.ac @@ -1374,8 +1374,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" -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 "$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 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..7c7a217d310 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 @@ -66,6 +67,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(wl_seat_get_keyboard(seat)); + else if (!(caps & WL_SEAT_CAPABILITY_KEYBOARD) && process_wayland.keyboard.wl_keyboard) + wayland_keyboard_deinit(); }
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..741dbfe1157 --- /dev/null +++ b/dlls/winewayland.drv/wayland_keyboard.c @@ -0,0 +1,274 @@ +/* + * 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 */ + /* FIXME: map a KEY to T5D / VK_EREOF */ + /* FIXME: map a KEY to 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 */ + /* FIXME: map a KEY to T6F / VK_OEM_PA3 */ + case KEY_COMPUTER: return 0x0071; /* T71 / VK_OEM_RESET */ + /* FIXME: map a KEY to T73 / VK_ABNT_C1 */ + case KEY_F24: return 0x0076; /* T76 / VK_F24 */ + case KEY_KPPLUSMINUS: return 0x007b; /* T7B / VK_OEM_PA1 */ + /* FIXME: map a KEY to T7C / VK_TAB */ + /* FIXME: map a KEY to T7E / VK_ABNT_C2 */ + /* FIXME: map a KEY to 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 HWND wayland_keyboard_get_focused_hwnd(void) +{ + struct wayland_keyboard *keyboard = &process_wayland.keyboard; + HWND hwnd; + + pthread_mutex_lock(&keyboard->mutex); + hwnd = keyboard->focused_hwnd; + pthread_mutex_unlock(&keyboard->mutex); + + return hwnd; +} + +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); + + pthread_mutex_lock(&keyboard->mutex); + keyboard->focused_hwnd = hwnd; + pthread_mutex_unlock(&keyboard->mutex); + + /* FIXME: update foreground window as well */ +} + +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); + + pthread_mutex_lock(&keyboard->mutex); + if (keyboard->focused_hwnd == hwnd) + keyboard->focused_hwnd = NULL; + pthread_mutex_unlock(&keyboard->mutex); + + /* 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) +{ + UINT scan = key2scan(key); + INPUT input = {0}; + HWND hwnd; + + if (!(hwnd = wayland_keyboard_get_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 wl_keyboard *wl_keyboard) +{ + struct wayland_keyboard *keyboard = &process_wayland.keyboard; + struct xkb_context *xkb_context = xkb_context_new(XKB_CONTEXT_NO_FLAGS); + + if (!xkb_context) + { + ERR("Failed to create XKB context\n"); + return; + } + + pthread_mutex_lock(&keyboard->mutex); + keyboard->wl_keyboard = wl_keyboard; + keyboard->xkb_context = xkb_context; + pthread_mutex_unlock(&keyboard->mutex); + wl_keyboard_add_listener(keyboard->wl_keyboard, &keyboard_listener, NULL); +} + +/*********************************************************************** + * wayland_keyboard_deinit + */ +void wayland_keyboard_deinit(void) +{ + struct wayland_keyboard *keyboard = &process_wayland.keyboard; + + pthread_mutex_lock(&keyboard->mutex); + if (keyboard->wl_keyboard) + { + wl_keyboard_destroy(keyboard->wl_keyboard); + keyboard->wl_keyboard = NULL; + } + if (keyboard->xkb_context) + { + xkb_context_unref(keyboard->xkb_context); + keyboard->xkb_context = NULL; + } + pthread_mutex_unlock(&keyboard->mutex); +} diff --git a/dlls/winewayland.drv/wayland_surface.c b/dlls/winewayland.drv/wayland_surface.c index ae4812ebb08..8bda2a5420b 100644 --- a/dlls/winewayland.drv/wayland_surface.c +++ b/dlls/winewayland.drv/wayland_surface.c @@ -181,6 +181,11 @@ void wayland_surface_destroy(struct wayland_surface *surface) } pthread_mutex_unlock(&process_wayland.pointer.mutex);
+ pthread_mutex_lock(&process_wayland.keyboard.mutex); + if (process_wayland.keyboard.focused_hwnd == surface->hwnd) + process_wayland.keyboard.focused_hwnd = NULL; + pthread_mutex_unlock(&process_wayland.keyboard.mutex); + 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..78ed68d3881 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,14 @@ 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; + pthread_mutex_t mutex; +}; + struct wayland_cursor { struct wayland_shm_buffer *shm_buffer; @@ -100,6 +109,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 +233,13 @@ 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 wl_keyboard *wl_keyboard) DECLSPEC_HIDDEN; +void wayland_keyboard_deinit(void) DECLSPEC_HIDDEN; + /********************************************************************** * Wayland pointer */
On Thu Nov 9 22:30:09 2023 +0000, Rémi Bernon wrote:
changed this line in [version 3 of the diff](/wine/wine/-/merge_requests/4204/diffs?diff_id=82561&start_sha=6c7af76bd0f3ef849dbc76005318cdda108d70ee#61204a3bfaf089b3588b75ee6440092f482a6098_263_266)
Well, only focused_hwnd is accessed concurrently and needs to be guarded, but I'll add locking there to make it similar to the pointer.
On Thu Nov 9 22:30:10 2023 +0000, Rémi Bernon wrote:
changed this line in [version 3 of the diff](/wine/wine/-/merge_requests/4204/diffs?diff_id=82561&start_sha=6c7af76bd0f3ef849dbc76005318cdda108d70ee#61204a3bfaf089b3588b75ee6440092f482a6098_165_165)
I don't have a good solution here. Changing focus has always been a pita and I believe win32u still has a race condition about it. I'm fine with not changing it from the driver side for now, and maybe that's the best thing to do.
v3: Add locking, drop `NtUserSetForegroundWindow` call.
On Thu Nov 9 22:30:20 2023 +0000, Rémi Bernon wrote:
I don't have a good solution here. Changing focus has always been a pita and I believe win32u still has a race condition about it. I'm fine with not changing it from the driver side for now, and maybe that's the best thing to do.
In the new version `focused_hwnd` is not cleared, but this is inconsequential for the current interactions.
This merge request was approved by Rémi Bernon.