From d027ebe3f05a73e1128302bd597e6a0c593fce91 Mon Sep 17 00:00:00 2001 From: Pierre Le Marre Date: Wed, 1 Nov 2023 13:06:38 +0100 Subject: [PATCH 1/2] interactive-wayland: Add support for Compose --- tools/interactive-evdev.c | 2 +- tools/interactive-wayland.c | 106 +++++++++++++++++++++++++---- tools/interactive-x11.c | 2 +- tools/tools-common.c | 6 +- tools/tools-common.h | 3 +- tools/xkbcli-interactive-wayland.1 | 3 + 6 files changed, 104 insertions(+), 18 deletions(-) diff --git a/tools/interactive-evdev.c b/tools/interactive-evdev.c index 2dece752e..66bc18cfb 100644 --- a/tools/interactive-evdev.c +++ b/tools/interactive-evdev.c @@ -280,7 +280,7 @@ process_event(struct keyboard *kbd, uint16_t type, uint16_t code, int32_t value) if (value != KEY_STATE_RELEASE) { tools_print_keycode_state( - kbd->state, kbd->compose_state, keycode, + NULL, kbd->state, kbd->compose_state, keycode, consumed_mode, print_fields ); } diff --git a/tools/interactive-wayland.c b/tools/interactive-wayland.c index 4c11e5c9f..bca33f86b 100644 --- a/tools/interactive-wayland.c +++ b/tools/interactive-wayland.c @@ -27,6 +27,7 @@ #include #include +#include #include #include #include @@ -36,7 +37,9 @@ #include #include "xkbcommon/xkbcommon.h" +#include "xkbcommon/xkbcommon-compose.h" #include "tools-common.h" +#include "src/utils.h" #include #include "xdg-shell-client-protocol.h" @@ -56,6 +59,7 @@ struct interactive_dpy { uint32_t shm_format; struct xkb_context *ctx; + struct xkb_compose_table *compose_table; struct wl_surface *wl_surf; struct xdg_surface *xdg_surf; @@ -76,6 +80,7 @@ struct interactive_seat { struct xkb_keymap *keymap; struct xkb_state *state; + struct xkb_compose_state *compose_state; struct wl_list link; }; @@ -394,17 +399,30 @@ kbd_key(void *data, struct wl_keyboard *wl_kbd, uint32_t serial, uint32_t time, uint32_t key, uint32_t state) { struct interactive_seat *seat = data; + xkb_keycode_t keycode = key + EVDEV_OFFSET; - if (state != WL_KEYBOARD_KEY_STATE_PRESSED) - return; + if (seat->compose_state && state != WL_KEYBOARD_KEY_STATE_RELEASED) { + xkb_keysym_t keysym = xkb_state_key_get_one_sym(seat->state, keycode); + xkb_compose_state_feed(seat->compose_state, keysym); + } - printf("%s: ", seat->name_str); - tools_print_keycode_state(seat->state, NULL, key + EVDEV_OFFSET, - XKB_CONSUMED_MODE_XKB, - PRINT_ALL_FIELDS); + if (state != WL_KEYBOARD_KEY_STATE_RELEASED) { + char *prefix = asprintf_safe("%s: ", seat->name_str); + tools_print_keycode_state(prefix, seat->state, seat->compose_state, keycode, + XKB_CONSUMED_MODE_XKB, + PRINT_ALL_FIELDS); + free(prefix); + } + + if (seat->compose_state) { + enum xkb_compose_status status = xkb_compose_state_get_status(seat->compose_state); + if (status == XKB_COMPOSE_CANCELLED || status == XKB_COMPOSE_COMPOSED) + xkb_compose_state_reset(seat->compose_state); + } /* Exit on ESC. */ - if (xkb_state_key_get_one_sym(seat->state, key + EVDEV_OFFSET) == XKB_KEY_Escape) + if (xkb_state_key_get_one_sym(seat->state, keycode) == XKB_KEY_Escape && + state != WL_KEYBOARD_KEY_STATE_PRESSED) terminate = true; } @@ -518,8 +536,10 @@ seat_capabilities(void *data, struct wl_seat *wl_seat, uint32_t caps) xkb_state_unref(seat->state); xkb_keymap_unref(seat->keymap); + xkb_compose_state_unref(seat->compose_state); seat->state = NULL; + seat->compose_state = NULL; seat->keymap = NULL; seat->wl_kbd = NULL; } @@ -564,6 +584,10 @@ seat_create(struct interactive_dpy *inter, struct wl_registry *registry, seat->wl_seat = wl_registry_bind(registry, name, &wl_seat_interface, MIN(version, 5)); wl_seat_add_listener(seat->wl_seat, &seat_listener, seat); + if (seat->inter->compose_table) { + seat->compose_state = xkb_compose_state_new(seat->inter->compose_table, + XKB_COMPOSE_STATE_NO_FLAGS); + } ret = asprintf(&seat->name_str, "seat:%d", wl_proxy_get_id((struct wl_proxy *) seat->wl_seat)); assert(ret >= 0); @@ -580,6 +604,7 @@ seat_destroy(struct interactive_seat *seat) wl_keyboard_destroy(seat->wl_kbd); xkb_state_unref(seat->state); + xkb_compose_state_unref(seat->compose_state); xkb_keymap_unref(seat->keymap); } @@ -673,19 +698,56 @@ dpy_disconnect(struct interactive_dpy *inter) wl_display_disconnect(inter->dpy); } +static void +usage(FILE *fp, char *progname) +{ + fprintf(fp, + "Usage: %s [--help] [--enable-compose]\n", + progname); + fprintf(fp, + " --enable-compose enable Compose\n" + " --help display this help and exit\n" + ); +} + int main(int argc, char *argv[]) { int ret; struct interactive_dpy inter; struct wl_registry *registry; - - if (argc != 1) { - ret = strcmp(argv[1], "--help"); - fprintf(ret ? stderr : stdout, "Usage: %s [--help]\n", argv[0]); - if (ret) - fprintf(stderr, "unrecognized option: %s\n", argv[1]); - return ret ? EXIT_INVALID_USAGE : EXIT_SUCCESS; + const char *locale; + struct xkb_compose_table *compose_table = NULL; + + bool with_compose = false; + enum options { + OPT_COMPOSE, + }; + static struct option opts[] = { + {"help", no_argument, 0, 'h'}, + {"enable-compose", no_argument, 0, OPT_COMPOSE}, + {0, 0, 0, 0}, + }; + + while (1) { + int opt; + int option_index = 0; + + opt = getopt_long(argc, argv, "h", opts, &option_index); + if (opt == -1) + break; + + switch (opt) { + case OPT_COMPOSE: + with_compose = true; + break; + case 'h': + usage(stdout, argv[0]); + return EXIT_SUCCESS; + case '?': + usage(stderr, argv[0]); + return EXIT_INVALID_USAGE; + } } setlocale(LC_ALL, ""); @@ -707,6 +769,20 @@ main(int argc, char *argv[]) goto err_out; } + if (with_compose) { + locale = setlocale(LC_CTYPE, NULL); + compose_table = + xkb_compose_table_new_from_locale(inter.ctx, locale, + XKB_COMPOSE_COMPILE_NO_FLAGS); + if (!compose_table) { + fprintf(stderr, "Couldn't create compose from locale\n"); + goto err_compose; + } + inter.compose_table = compose_table; + } else { + inter.compose_table = NULL; + } + registry = wl_display_get_registry(inter.dpy); wl_registry_add_listener(registry, ®istry_listener, &inter); @@ -738,6 +814,8 @@ main(int argc, char *argv[]) wl_registry_destroy(registry); err_conn: dpy_disconnect(&inter); +err_compose: + xkb_compose_table_unref(compose_table); err_out: exit(ret >= 0 ? EXIT_SUCCESS : EXIT_FAILURE); } diff --git a/tools/interactive-x11.c b/tools/interactive-x11.c index 9fe0c10d2..290967e81 100644 --- a/tools/interactive-x11.c +++ b/tools/interactive-x11.c @@ -242,7 +242,7 @@ process_event(xcb_generic_event_t *gevent, struct keyboard *kbd) xcb_key_press_event_t *event = (xcb_key_press_event_t *) gevent; xkb_keycode_t keycode = event->detail; - tools_print_keycode_state(kbd->state, NULL, keycode, + tools_print_keycode_state(NULL, kbd->state, NULL, keycode, XKB_CONSUMED_MODE_XKB, PRINT_ALL_FIELDS); diff --git a/tools/tools-common.c b/tools/tools-common.c index d48f74a4b..b163438fe 100644 --- a/tools/tools-common.c +++ b/tools/tools-common.c @@ -143,7 +143,8 @@ print_keys_modmaps(struct xkb_keymap *keymap) { #endif void -tools_print_keycode_state(struct xkb_state *state, +tools_print_keycode_state(char *prefix, + struct xkb_state *state, struct xkb_compose_state *compose_state, xkb_keycode_t keycode, enum xkb_consumed_mode consumed_mode, @@ -182,6 +183,9 @@ tools_print_keycode_state(struct xkb_state *state, syms = &sym; } + if (prefix) + printf("%s", prefix); + print_keycode(keymap, "keycode [ ", keycode, " ] "); #ifdef ENABLE_PRIVATE_APIS diff --git a/tools/tools-common.h b/tools/tools-common.h index cc7771bc4..1ff6c5a6f 100644 --- a/tools/tools-common.h +++ b/tools/tools-common.h @@ -61,7 +61,8 @@ print_keys_modmaps(struct xkb_keymap *keymap); #endif void -tools_print_keycode_state(struct xkb_state *state, +tools_print_keycode_state(char *prefix, + struct xkb_state *state, struct xkb_compose_state *compose_state, xkb_keycode_t keycode, enum xkb_consumed_mode consumed_mode, diff --git a/tools/xkbcli-interactive-wayland.1 b/tools/xkbcli-interactive-wayland.1 index d9ba1de85..0542e07d8 100644 --- a/tools/xkbcli-interactive-wayland.1 +++ b/tools/xkbcli-interactive-wayland.1 @@ -28,6 +28,9 @@ This is a debugging tool, its behavior or output is not guaranteed to be stable. .Bl -tag -width Ds .It Fl \-help Print help and exit +. +.It Fl \-enable\-compose +Enable Compose functionality .El . .Sh SEE ALSO From 546228a0956d812017b02d7999be84b07d8fa318 Mon Sep 17 00:00:00 2001 From: Pierre Le Marre Date: Wed, 1 Nov 2023 18:12:15 +0100 Subject: [PATCH 2/2] interactive-x11: Add support for Compose --- tools/interactive-x11.c | 93 ++++++++++++++++++++++++++++++---- tools/xkbcli-interactive-x11.1 | 3 ++ 2 files changed, 87 insertions(+), 9 deletions(-) diff --git a/tools/interactive-x11.c b/tools/interactive-x11.c index 290967e81..0ab1898db 100644 --- a/tools/interactive-x11.c +++ b/tools/interactive-x11.c @@ -23,6 +23,7 @@ #include "config.h" +#include #include #include #include @@ -31,6 +32,7 @@ #include #include "xkbcommon/xkbcommon-x11.h" +#include "xkbcommon/xkbcommon-compose.h" #include "tools-common.h" /* @@ -58,6 +60,7 @@ struct keyboard { struct xkb_keymap *keymap; struct xkb_state *state; + struct xkb_compose_state *compose_state; int32_t device_id; }; @@ -153,7 +156,8 @@ update_keymap(struct keyboard *kbd) static int init_kbd(struct keyboard *kbd, xcb_connection_t *conn, uint8_t first_xkb_event, - int32_t device_id, struct xkb_context *ctx) + int32_t device_id, struct xkb_context *ctx, + struct xkb_compose_table *compose_table) { int ret; @@ -162,11 +166,15 @@ init_kbd(struct keyboard *kbd, xcb_connection_t *conn, uint8_t first_xkb_event, kbd->ctx = ctx; kbd->keymap = NULL; kbd->state = NULL; + kbd->compose_state = NULL; kbd->device_id = device_id; ret = update_keymap(kbd); if (ret) goto err_out; + if (compose_table) + kbd->compose_state = xkb_compose_state_new(compose_table, + XKB_COMPOSE_STATE_NO_FLAGS); ret = select_xkb_events_for_device(conn, device_id); if (ret) @@ -176,6 +184,7 @@ init_kbd(struct keyboard *kbd, xcb_connection_t *conn, uint8_t first_xkb_event, err_state: xkb_state_unref(kbd->state); + xkb_compose_state_unref(kbd->compose_state); xkb_keymap_unref(kbd->keymap); err_out: return -1; @@ -185,6 +194,7 @@ static void deinit_kbd(struct keyboard *kbd) { xkb_state_unref(kbd->state); + xkb_compose_state_unref(kbd->compose_state); xkb_keymap_unref(kbd->keymap); } @@ -242,10 +252,22 @@ process_event(xcb_generic_event_t *gevent, struct keyboard *kbd) xcb_key_press_event_t *event = (xcb_key_press_event_t *) gevent; xkb_keycode_t keycode = event->detail; - tools_print_keycode_state(NULL, kbd->state, NULL, keycode, + if (kbd->compose_state) { + xkb_keysym_t keysym = xkb_state_key_get_one_sym(kbd->state, keycode); + xkb_compose_state_feed(kbd->compose_state, keysym); + } + + tools_print_keycode_state(NULL, kbd->state, kbd->compose_state, keycode, XKB_CONSUMED_MODE_XKB, PRINT_ALL_FIELDS); + if (kbd->compose_state) { + enum xkb_compose_status status = xkb_compose_state_get_status(kbd->compose_state); + if (status == XKB_COMPOSE_CANCELLED || + status == XKB_COMPOSE_COMPOSED) + xkb_compose_state_reset(kbd->compose_state); + } + /* Exit on ESC. */ if (xkb_state_key_get_one_sym(kbd->state, keycode) == XKB_KEY_Escape) terminate = true; @@ -328,6 +350,18 @@ create_capture_window(xcb_connection_t *conn) return 0; } +static void +usage(FILE *fp, char *progname) +{ + fprintf(fp, + "Usage: %s [--help] [--enable-compose]\n", + progname); + fprintf(fp, + " --enable-compose enable Compose\n" + " --help display this help and exit\n" + ); +} + int main(int argc, char *argv[]) { @@ -337,13 +371,38 @@ main(int argc, char *argv[]) int32_t core_kbd_device_id; struct xkb_context *ctx; struct keyboard core_kbd; + const char *locale; + struct xkb_compose_table *compose_table = NULL; - if (argc != 1) { - ret = strcmp(argv[1], "--help"); - fprintf(ret ? stderr : stdout, "Usage: %s [--help]\n", argv[0]); - if (ret) - fprintf(stderr, "unrecognized option: %s\n", argv[1]); - return ret ? EXIT_INVALID_USAGE : EXIT_SUCCESS; + bool with_compose = false; + enum options { + OPT_COMPOSE, + }; + static struct option opts[] = { + {"help", no_argument, 0, 'h'}, + {"enable-compose", no_argument, 0, OPT_COMPOSE}, + {0, 0, 0, 0}, + }; + + while (1) { + int opt; + int option_index = 0; + + opt = getopt_long(argc, argv, "h", opts, &option_index); + if (opt == -1) + break; + + switch (opt) { + case OPT_COMPOSE: + with_compose = true; + break; + case 'h': + usage(stdout, argv[0]); + return EXIT_SUCCESS; + case '?': + usage(stderr, argv[0]); + return EXIT_INVALID_USAGE; + } } setlocale(LC_ALL, ""); @@ -373,6 +432,19 @@ main(int argc, char *argv[]) goto err_conn; } + if (with_compose) { + locale = setlocale(LC_CTYPE, NULL); + compose_table = + xkb_compose_table_new_from_locale(ctx, locale, + XKB_COMPOSE_COMPILE_NO_FLAGS); + if (!compose_table) { + fprintf(stderr, "Couldn't create compose from locale\n"); + goto err_compose; + } + } else { + compose_table = NULL; + } + core_kbd_device_id = xkb_x11_get_core_keyboard_device_id(conn); if (core_kbd_device_id == -1) { ret = -1; @@ -380,7 +452,8 @@ main(int argc, char *argv[]) goto err_ctx; } - ret = init_kbd(&core_kbd, conn, first_xkb_event, core_kbd_device_id, ctx); + ret = init_kbd(&core_kbd, conn, first_xkb_event, core_kbd_device_id, + ctx, compose_table); if (ret) { fprintf(stderr, "Couldn't initialize core keyboard device\n"); goto err_ctx; @@ -396,6 +469,8 @@ main(int argc, char *argv[]) ret = loop(conn, &core_kbd); tools_enable_stdin_echo(); +err_compose: + xkb_compose_table_unref(compose_table); err_core_kbd: deinit_kbd(&core_kbd); err_ctx: diff --git a/tools/xkbcli-interactive-x11.1 b/tools/xkbcli-interactive-x11.1 index 0f16f3f8b..dbbd25b34 100644 --- a/tools/xkbcli-interactive-x11.1 +++ b/tools/xkbcli-interactive-x11.1 @@ -28,6 +28,9 @@ This is a debugging tool, its behavior or output is not guaranteed to be stable. .Bl -tag -width Ds .It Fl \-help Print help and exit +. +.It Fl \-enable\-compose +Enable Compose functionality .El . .Sh SEE ALSO