From 70457604900eec31cf988b716f7b4518ab7d6927 Mon Sep 17 00:00:00 2001 From: Quantum Date: Sat, 14 Aug 2021 00:20:34 -0400 Subject: [PATCH] [client] wayland: add keyboard typing handling with xkbcommon --- client/displayservers/Wayland/CMakeLists.txt | 1 + client/displayservers/Wayland/input.c | 84 +++++++++++++++++++- client/displayservers/Wayland/wayland.h | 7 ++ 3 files changed, 91 insertions(+), 1 deletion(-) diff --git a/client/displayservers/Wayland/CMakeLists.txt b/client/displayservers/Wayland/CMakeLists.txt index c2586b10..4425d7ad 100644 --- a/client/displayservers/Wayland/CMakeLists.txt +++ b/client/displayservers/Wayland/CMakeLists.txt @@ -5,6 +5,7 @@ find_package(PkgConfig) pkg_check_modules(DISPLAYSERVER_Wayland REQUIRED IMPORTED_TARGET wayland-client wayland-cursor + xkbcommon ) set(DISPLAYSERVER_Wayland_OPT_PKGCONFIG_LIBRARIES "") diff --git a/client/displayservers/Wayland/input.c b/client/displayservers/Wayland/input.c index 92d7493e..630bd7fa 100644 --- a/client/displayservers/Wayland/input.c +++ b/client/displayservers/Wayland/input.c @@ -20,11 +20,14 @@ #include "wayland.h" +#include #include #include +#include #include #include +#include #include "app.h" #include "common/debug.h" @@ -159,6 +162,50 @@ static const struct zwp_relative_pointer_v1_listener relativePointerListener = { static void keyboardKeymapHandler(void * data, struct wl_keyboard * keyboard, uint32_t format, int fd, uint32_t size) { + if (!wlWm.xkb) + goto done; + + if (wlWm.keymap) + { + xkb_keymap_unref(wlWm.keymap); + wlWm.keymap = NULL; + } + + if (wlWm.xkbState) + { + xkb_state_unref(wlWm.xkbState); + wlWm.xkbState = NULL; + } + + if (format != WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1) + { + DEBUG_WARN("Unsupported keymap format, keyboard input will not work: %d", format); + goto done; + } + + char * map = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0); + if (map == MAP_FAILED) + { + DEBUG_ERROR("Failed to mmap keymap: %s", strerror(errno)); + goto done; + } + + wlWm.keymap = xkb_keymap_new_from_string(wlWm.xkb, map, + XKB_KEYMAP_FORMAT_TEXT_V1, XKB_KEYMAP_COMPILE_NO_FLAGS); + + if (!wlWm.keymap) + DEBUG_WARN("Failed to load keymap, keyboard input will not work"); + + munmap(map, size); + + if (wlWm.keymap) + { + wlWm.xkbState = xkb_state_new(wlWm.keymap); + if (!wlWm.xkbState) + DEBUG_WARN("Failed to create xkb_state"); + } + +done: close(fd); } @@ -198,13 +245,35 @@ static void keyboardKeyHandler(void * data, struct wl_keyboard * keyboard, app_handleKeyPress(key); else app_handleKeyRelease(key); + + if (!wlWm.xkbState || !app_isOverlayMode() || state != WL_KEYBOARD_KEY_STATE_PRESSED) + return; + + key += 8; // xkb scancode is evdev scancode + 8 + int size = xkb_state_key_get_utf8(wlWm.xkbState, key, NULL, 0); + + if (size <= 0) + return; + + char buffer[size + 1]; + xkb_state_key_get_utf8(wlWm.xkbState, key, buffer, size + 1); + app_handleKeyboardTyped(buffer); } static void keyboardModifiersHandler(void * data, struct wl_keyboard * keyboard, uint32_t serial, uint32_t modsDepressed, uint32_t modsLatched, uint32_t modsLocked, uint32_t group) { - // Do nothing. + if (!wlWm.xkbState) + return; + + xkb_state_update_mask(wlWm.xkbState, modsDepressed, modsLatched, modsLocked, 0, 0, group); + app_handleKeyboardModifiers( + xkb_state_mod_name_is_active(wlWm.xkbState, XKB_MOD_NAME_CTRL, XKB_STATE_MODS_EFFECTIVE) > 0, + xkb_state_mod_name_is_active(wlWm.xkbState, XKB_MOD_NAME_SHIFT, XKB_STATE_MODS_EFFECTIVE) > 0, + xkb_state_mod_name_is_active(wlWm.xkbState, XKB_MOD_NAME_ALT, XKB_STATE_MODS_EFFECTIVE) > 0, + xkb_state_mod_name_is_active(wlWm.xkbState, XKB_MOD_NAME_LOGO, XKB_STATE_MODS_EFFECTIVE) > 0 + ); } static const struct wl_keyboard_listener keyboardListener = { @@ -293,6 +362,10 @@ bool waylandInputInit(void) DEBUG_WARN("zwp_keyboard_shortcuts_inhibit_manager_v1 not exported by " "compositor, keyboard will not be grabbed"); + wlWm.xkb = xkb_context_new(XKB_CONTEXT_NO_FLAGS); + if (!wlWm.xkb) + DEBUG_WARN("Failed to initialize xkb, keyboard input will not work"); + wl_seat_add_listener(wlWm.seat, &seatListener, NULL); wl_display_roundtrip(wlWm.display); @@ -317,6 +390,15 @@ void waylandInputFree(void) wl_pointer_destroy(wlWm.pointer); wl_keyboard_destroy(wlWm.keyboard); wl_seat_destroy(wlWm.seat); + + if (wlWm.xkbState) + xkb_state_unref(wlWm.xkbState); + + if (wlWm.keymap) + xkb_keymap_unref(wlWm.keymap); + + if (wlWm.xkb) + xkb_context_unref(wlWm.xkb); } void waylandGrabPointer(void) diff --git a/client/displayservers/Wayland/wayland.h b/client/displayservers/Wayland/wayland.h index 2c7d3b8d..e454bd91 100644 --- a/client/displayservers/Wayland/wayland.h +++ b/client/displayservers/Wayland/wayland.h @@ -88,6 +88,10 @@ enum EGLSwapWithDamageState { SWAP_WITH_DAMAGE_EXT, }; +struct xkb_context; +struct xkb_keymap; +struct xkb_state; + struct WaylandDSState { bool pointerGrabbed; @@ -155,6 +159,9 @@ struct WaylandDSState struct zwp_keyboard_shortcuts_inhibit_manager_v1 * keyboardInhibitManager; struct zwp_keyboard_shortcuts_inhibitor_v1 * keyboardInhibitor; uint32_t keyboardEnterSerial; + struct xkb_context * xkb; + struct xkb_state * xkbState; + struct xkb_keymap * keymap; struct wl_pointer * pointer; struct zwp_relative_pointer_manager_v1 * relativePointerManager;