From 5c78df9c2337746bb929337239d000f8a2e6560f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luis=20C=C3=A1ceres?= Date: Wed, 14 Apr 2021 00:56:50 +0100 Subject: [PATCH] Support key composing (i.e. dead keys) in Wayland driver (#4296) Based on an old patch by chw from the old Bugzilla issue tracker. Authored-by: chw Co-authored-by: Sam Lantinga --- src/video/wayland/SDL_waylanddyn.h | 1 + src/video/wayland/SDL_waylandevents.c | 56 +++++++++++++++++++++++-- src/video/wayland/SDL_waylandevents_c.h | 2 + src/video/wayland/SDL_waylandsym.h | 8 ++++ 4 files changed, 64 insertions(+), 3 deletions(-) diff --git a/src/video/wayland/SDL_waylanddyn.h b/src/video/wayland/SDL_waylanddyn.h index 63b831f0a..55ac1ebf7 100644 --- a/src/video/wayland/SDL_waylanddyn.h +++ b/src/video/wayland/SDL_waylanddyn.h @@ -38,6 +38,7 @@ struct wl_shm; #include "wayland-cursor.h" #include "wayland-util.h" #include "xkbcommon/xkbcommon.h" +#include "xkbcommon/xkbcommon-compose.h" #ifdef __cplusplus extern "C" diff --git a/src/video/wayland/SDL_waylandevents.c b/src/video/wayland/SDL_waylandevents.c index 73b81c1e0..2657a276e 100644 --- a/src/video/wayland/SDL_waylandevents.c +++ b/src/video/wayland/SDL_waylandevents.c @@ -57,6 +57,7 @@ #include #include #include +#include /* Weston uses a ratio of 10 units per scroll tick */ #define WAYLAND_WHEEL_AXIS_UNIT 10 @@ -624,7 +625,7 @@ keyboard_handle_keymap(void *data, struct wl_keyboard *keyboard, uint32_t format, int fd, uint32_t size) { struct SDL_WaylandInput *input = data; - char *map_str; + char *map_str, *locale; if (!data) { close(fd); @@ -661,6 +662,30 @@ keyboard_handle_keymap(void *data, struct wl_keyboard *keyboard, input->xkb.keymap = NULL; return; } + + /* + * See https://blogs.s-osg.org/compose-key-support-weston/ + * for further explanation on dead keys in Wayland. + */ + + /* Look up the preferred locale, falling back to "C" as default */ + if (!(locale = SDL_getenv("LC_ALL"))) + if (!(locale = SDL_getenv("LC_CTYPE"))) + if (!(locale = SDL_getenv("LANG"))) + locale = "C"; + /* Set up XKB compose table */ + input->xkb.compose_table = WAYLAND_xkb_compose_table_new_from_locale(input->display->xkb_context, + locale, XKB_COMPOSE_COMPILE_NO_FLAGS); + if (input->xkb.compose_table) { + /* Set up XKB compose state */ + input->xkb.compose_state = WAYLAND_xkb_compose_state_new(input->xkb.compose_table, + XKB_COMPOSE_STATE_NO_FLAGS); + if (!input->xkb.compose_state) { + fprintf(stderr, "could not create XKB compose state\n"); + WAYLAND_xkb_compose_table_unref(input->xkb.compose_table); + input->xkb.compose_table = NULL; + } + } } static void @@ -709,6 +734,7 @@ keyboard_input_get_text(char text[8], const struct SDL_WaylandInput *input, uint { SDL_WindowData *window = input->keyboard_focus; const xkb_keysym_t *syms; + xkb_keysym_t sym; if (!window || window->keyboard_device != input || !input->xkb.state) { return SDL_FALSE; @@ -718,15 +744,33 @@ keyboard_input_get_text(char text[8], const struct SDL_WaylandInput *input, uint if (WAYLAND_xkb_state_key_get_syms(input->xkb.state, key + 8, &syms) != 1) { return SDL_FALSE; } + sym = syms[0]; #ifdef SDL_USE_IME - if (SDL_IME_ProcessKeyEvent(syms[0], key + 8)) { + if (SDL_IME_ProcessKeyEvent(sym, key + 8)) { *handled_by_ime = SDL_TRUE; return SDL_TRUE; } #endif - return WAYLAND_xkb_keysym_to_utf8(syms[0], text, 8) > 0; + if (WAYLAND_xkb_compose_state_feed(input->xkb.compose_state, sym) == XKB_COMPOSE_FEED_ACCEPTED) { + switch(WAYLAND_xkb_compose_state_get_status(input->xkb.compose_state)) { + case XKB_COMPOSE_COMPOSING: + *handled_by_ime = SDL_TRUE; + return SDL_TRUE; + case XKB_COMPOSE_CANCELLED: + default: + sym = XKB_KEY_NoSymbol; + break; + case XKB_COMPOSE_NOTHING: + break; + case XKB_COMPOSE_COMPOSED: + sym = WAYLAND_xkb_compose_state_get_one_sym(input->xkb.compose_state); + break; + } + } + + return WAYLAND_xkb_keysym_to_utf8(sym, text, 8) > 0; } static void @@ -1239,6 +1283,12 @@ void Wayland_display_destroy_input(SDL_VideoData *d) if (input->seat) wl_seat_destroy(input->seat); + if (input->xkb.compose_state) + WAYLAND_xkb_compose_state_unref(input->xkb.compose_state); + + if (input->xkb.compose_table) + WAYLAND_xkb_compose_table_unref(input->xkb.compose_table); + if (input->xkb.state) WAYLAND_xkb_state_unref(input->xkb.state); diff --git a/src/video/wayland/SDL_waylandevents_c.h b/src/video/wayland/SDL_waylandevents_c.h index ac4fcceb3..f3604ac36 100644 --- a/src/video/wayland/SDL_waylandevents_c.h +++ b/src/video/wayland/SDL_waylandevents_c.h @@ -64,6 +64,8 @@ struct SDL_WaylandInput { struct { struct xkb_keymap *keymap; struct xkb_state *state; + struct xkb_compose_table *compose_table; + struct xkb_compose_state *compose_state; } xkb; /* information about axis events on current frame */ diff --git a/src/video/wayland/SDL_waylandsym.h b/src/video/wayland/SDL_waylandsym.h index 5e9bafefa..278c4ff17 100644 --- a/src/video/wayland/SDL_waylandsym.h +++ b/src/video/wayland/SDL_waylandsym.h @@ -118,6 +118,14 @@ SDL_WAYLAND_SYM(enum xkb_state_component, xkb_state_update_mask, (struct xkb_sta xkb_layout_index_t depressed_layout,\ xkb_layout_index_t latched_layout,\ xkb_layout_index_t locked_layout) ) +SDL_WAYLAND_SYM(struct xkb_compose_table *, xkb_compose_table_new_from_locale, (struct xkb_context *,\ + const char *locale, enum xkb_compose_compile_flags) ) +SDL_WAYLAND_SYM(void, xkb_compose_table_unref, (struct xkb_compose_table *) ) +SDL_WAYLAND_SYM(struct xkb_compose_state *, xkb_compose_state_new, (struct xkb_compose_table *, enum xkb_compose_state_flags) ) +SDL_WAYLAND_SYM(void, xkb_compose_state_unref, (struct xkb_compose_state *) ) +SDL_WAYLAND_SYM(enum xkb_compose_feed_result, xkb_compose_state_feed, (struct xkb_compose_state *, xkb_keysym_t) ) +SDL_WAYLAND_SYM(enum xkb_compose_status, xkb_compose_state_get_status, (struct xkb_compose_state *) ) +SDL_WAYLAND_SYM(xkb_keysym_t, xkb_compose_state_get_one_sym, (struct xkb_compose_state *) ) SDL_WAYLAND_SYM(void, xkb_keymap_key_for_each, (struct xkb_keymap *, xkb_keymap_key_iter_t, void*) ) SDL_WAYLAND_SYM(int, xkb_keymap_key_get_syms_by_level, (struct xkb_keymap *, xkb_keycode_t,