From 5c78df9c2337746bb929337239d000f8a2e6560f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Luis=20C=C3=A1ceres?= <[EMAIL REDACTED]>
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 <slouken@libsdl.org>
---
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 <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 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,