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>
This commit is contained in:
Luis Cáceres 2021-04-14 00:56:50 +01:00 committed by GitHub
parent b04136e75e
commit 5c78df9c23
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 64 additions and 3 deletions

View File

@ -38,6 +38,7 @@ struct wl_shm;
#include "wayland-cursor.h" #include "wayland-cursor.h"
#include "wayland-util.h" #include "wayland-util.h"
#include "xkbcommon/xkbcommon.h" #include "xkbcommon/xkbcommon.h"
#include "xkbcommon/xkbcommon-compose.h"
#ifdef __cplusplus #ifdef __cplusplus
extern "C" extern "C"

View File

@ -57,6 +57,7 @@
#include <poll.h> #include <poll.h>
#include <unistd.h> #include <unistd.h>
#include <xkbcommon/xkbcommon.h> #include <xkbcommon/xkbcommon.h>
#include <xkbcommon/xkbcommon-compose.h>
/* Weston uses a ratio of 10 units per scroll tick */ /* Weston uses a ratio of 10 units per scroll tick */
#define WAYLAND_WHEEL_AXIS_UNIT 10 #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) uint32_t format, int fd, uint32_t size)
{ {
struct SDL_WaylandInput *input = data; struct SDL_WaylandInput *input = data;
char *map_str; char *map_str, *locale;
if (!data) { if (!data) {
close(fd); close(fd);
@ -661,6 +662,30 @@ keyboard_handle_keymap(void *data, struct wl_keyboard *keyboard,
input->xkb.keymap = NULL; input->xkb.keymap = NULL;
return; 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 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; SDL_WindowData *window = input->keyboard_focus;
const xkb_keysym_t *syms; const xkb_keysym_t *syms;
xkb_keysym_t sym;
if (!window || window->keyboard_device != input || !input->xkb.state) { if (!window || window->keyboard_device != input || !input->xkb.state) {
return SDL_FALSE; 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) { if (WAYLAND_xkb_state_key_get_syms(input->xkb.state, key + 8, &syms) != 1) {
return SDL_FALSE; return SDL_FALSE;
} }
sym = syms[0];
#ifdef SDL_USE_IME #ifdef SDL_USE_IME
if (SDL_IME_ProcessKeyEvent(syms[0], key + 8)) { if (SDL_IME_ProcessKeyEvent(sym, key + 8)) {
*handled_by_ime = SDL_TRUE; *handled_by_ime = SDL_TRUE;
return SDL_TRUE; return SDL_TRUE;
} }
#endif #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 static void
@ -1239,6 +1283,12 @@ void Wayland_display_destroy_input(SDL_VideoData *d)
if (input->seat) if (input->seat)
wl_seat_destroy(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) if (input->xkb.state)
WAYLAND_xkb_state_unref(input->xkb.state); WAYLAND_xkb_state_unref(input->xkb.state);

View File

@ -64,6 +64,8 @@ struct SDL_WaylandInput {
struct { struct {
struct xkb_keymap *keymap; struct xkb_keymap *keymap;
struct xkb_state *state; struct xkb_state *state;
struct xkb_compose_table *compose_table;
struct xkb_compose_state *compose_state;
} xkb; } xkb;
/* information about axis events on current frame */ /* information about axis events on current frame */

View File

@ -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 depressed_layout,\
xkb_layout_index_t latched_layout,\ xkb_layout_index_t latched_layout,\
xkb_layout_index_t locked_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(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 *, SDL_WAYLAND_SYM(int, xkb_keymap_key_get_syms_by_level, (struct xkb_keymap *,
xkb_keycode_t, xkb_keycode_t,