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 <slouken@libsdl.org>
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153
diff --git a/src/video/wayland/SDL_waylanddyn.h b/src/video/wayland/SDL_waylanddyn.h
index 63b831f..55ac1eb 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 73b81c1..2657a27 100644
--- a/src/video/wayland/SDL_waylandevents.c
+++ b/src/video/wayland/SDL_waylandevents.c
@@ -57,6 +57,7 @@
#include <poll.h>
#include <unistd.h>
#include <xkbcommon/xkbcommon.h>
+#include <xkbcommon/xkbcommon-compose.h>
/* 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 ac4fcce..f3604ac 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 5e9bafe..278c4ff 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,