From 3215d54f31c9e339598a34c9fa43f3dc3c5d1e42 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Mon, 27 Mar 2023 16:56:10 +0200 Subject: [PATCH] input: (kitty kbd): the resulting UTF-8 string may translate to multiple UTF-32 codepoints MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When this happened (for example, by specifying a custom compose sequence), the kitty keyboard protocol didn’t emit any text at all. This was caused by the utf32 codepoint being -1. This in turned was caused by us trying to convert the utf8 sequence to a *single* utf32 codepoint. This patch replaces the use of mbrtoc32() with a call to ambstoc32(), and the utf32 codepoint with an utf32 string. The kitty keyboard protocol is updated: * When determining if we’re dealing with text, check *all* codepoints in the utf32 string. * Add support for multiple codepoints when reporting "associated text". The first codepoint is the actual parameter in the emitted sequence, and the remaining codepoints are sub-parameters. I.e. the codepoints are colon separated. Closes #1288 --- CHANGELOG.md | 3 +++ input.c | 41 +++++++++++++++++++++++++++++------------ 2 files changed, 32 insertions(+), 12 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 01d664fc..a02b9346 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -134,6 +134,8 @@ * `xdg\_toplevel::set_min_size()` not being called. * Key bindings with consumed modifiers masking other key bindings ([#1280][1280]). +* Multi-character compose sequences with the kitty keyboard protocol + ([#1288][1288]). [1173]: https://codeberg.org/dnkl/foot/issues/1173 [1190]: https://codeberg.org/dnkl/foot/issues/1190 @@ -144,6 +146,7 @@ [1256]: https://codeberg.org/dnkl/foot/issues/1256 [1249]: https://codeberg.org/dnkl/foot/issues/1249 [1280]: https://codeberg.org/dnkl/foot/issues/1280 +[1288]: https://codeberg.org/dnkl/foot/issues/1288 ### Security diff --git a/input.c b/input.c index 0a3773bc..b7f25670 100644 --- a/input.c +++ b/input.c @@ -898,7 +898,7 @@ struct kbd_ctx { const uint8_t *buf; size_t count; } utf8; - uint32_t utf32; + uint32_t *utf32; enum xkb_compose_status compose_status; enum wl_keyboard_key_state key_state; @@ -1121,12 +1121,18 @@ kitty_kbd_protocol(struct seat *seat, struct terminal *term, (seat->kbd.mod_num != XKB_MOD_INVALID ? 1 << seat->kbd.mod_num : 0); const xkb_keysym_t sym = ctx->sym; - const uint32_t utf32 = ctx->utf32; + const uint32_t *utf32 = ctx->utf32; const uint8_t *const utf8 = ctx->utf8.buf; - - const bool is_text = iswprint(utf32) && (effective & ~caps_num) == 0; const size_t count = ctx->utf8.count; + bool is_text = utf32 != NULL && (effective & ~caps_num) == 0; + for (size_t i = 0; utf32[i] != U'\0'; i++) { + if (!iswprint(utf32[i])) { + is_text = false; + break; + } + } + const bool report_associated_text = (flags & KITTY_KBD_REPORT_ASSOCIATED) && is_text && !released; @@ -1245,7 +1251,7 @@ emit_escapes: : sym; if (composed) - key = utf32; + key = utf32[0]; /* TODO: what if there are multiple codepoints? */ else { key = xkb_keysym_to_utf32(sym_to_use); if (key == 0) @@ -1284,7 +1290,7 @@ emit_escapes: } else event[0] = '\0'; - char buf[64], *p = buf; + char buf[128], *p = buf; size_t left = sizeof(buf); size_t bytes; @@ -1316,8 +1322,16 @@ emit_escapes: } if (report_associated_text) { - bytes = snprintf(p, left, "%s;%u", !emit_mods ? ";" : "", utf32); + bytes = snprintf(p, left, "%s;%u", !emit_mods ? ";" : "", utf32[0]); p += bytes; left -= bytes; + + /* Additional text codepoints */ + if (utf32[0] != U'\0') { + for (size_t i = 1; utf32[i] != U'\0'; i++) { + bytes = snprintf(p, left, ":%u", utf32[i]); + p += bytes; left -= bytes; + } + } } bytes = snprintf(p, left, "%c", final); @@ -1514,19 +1528,20 @@ key_press_release(struct seat *seat, struct terminal *term, uint32_t serial, * and use a malloc:ed buffer when necessary */ uint8_t buf[32]; uint8_t *utf8 = count < sizeof(buf) ? buf : xmalloc(count + 1); - uint32_t utf32 = (uint32_t)-1; + uint32_t *utf32 = NULL; if (composed) { xkb_compose_state_get_utf8( seat->kbd.xkb_compose_state, (char *)utf8, count + 1); - char32_t wc; - if (mbrtoc32(&wc, (const char *)utf8, count, &(mbstate_t){0}) == count) - utf32 = wc; + if (count > 0) + utf32 = ambstoc32((const char *)utf8); } else { xkb_state_key_get_utf8( seat->kbd.xkb_state, key, (char *)utf8, count + 1); - utf32 = xkb_state_key_get_utf32(seat->kbd.xkb_state, key); + + utf32 = xcalloc(2, sizeof(utf32[0])); + utf32[0] = xkb_state_key_get_utf32(seat->kbd.xkb_state, key); } struct kbd_ctx ctx = { @@ -1563,6 +1578,8 @@ key_press_release(struct seat *seat, struct terminal *term, uint32_t serial, selection_cancel(term); } + free(utf32); + maybe_repeat: clock_gettime( term->wl->presentation_clock_id, &term->render.input_time);