From 88dcde3ed8b30c14a5d834d0e88f9b4a5f584939 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Thu, 6 Feb 2025 07:31:30 +0100 Subject: [PATCH] term: insert-mode: handle combining characters correctly When the client application emits combining characters, for example multi-codepoint emojis, in insert-mode, we ended up pushing partial graphemes to the right, for each codepoint, resulting in too many cells (and with the wrong content) being inserted. The fix is fairly simple; don't "insert" when appending characters to an existing grapheme cluster. This isn't something we can detect easily in print_insert() (it would require us to do grapheme clustering again). Fortunately, we do have the required information in action_utf8_print(). So, pass this information as a boolean to term_print(). Closes #1947 --- CHANGELOG.md | 4 ++++ csi.c | 2 +- terminal.c | 7 ++++--- terminal.h | 3 ++- vt.c | 4 +++- 5 files changed, 14 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 05bda33c..b9b89bf3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -105,9 +105,13 @@ ([#1929][1929]). * Foot not closing file descriptors for unrecognized or `no_keymap` keymaps. +* Combining characters (including emojis consisting of multiple + codepoints) not being handled correctly when _insert mode_ is + enabled ([#1947][1947]). [1918]: https://codeberg.org/dnkl/foot/issues/1918 [1929]: https://codeberg.org/dnkl/foot/issues/1929 +[1947]: https://codeberg.org/dnkl/foot/issues/1947 ### Security diff --git a/csi.c b/csi.c index 61cbdced..b982023c 100644 --- a/csi.c +++ b/csi.c @@ -793,7 +793,7 @@ csi_dispatch(struct terminal *term, uint8_t final) const int width = c32width(term->vt.last_printed); if (width > 0) { for (int i = 0; i < count; i++) - term_print(term, term->vt.last_printed, width); + term_print(term, term->vt.last_printed, width, false); } } break; diff --git a/terminal.c b/terminal.c index 9fd20002..b88a794e 100644 --- a/terminal.c +++ b/terminal.c @@ -3896,7 +3896,7 @@ term_fill(struct terminal *term, int r, int c, uint8_t data, size_t count, } void -term_print(struct terminal *term, char32_t wc, int width) +term_print(struct terminal *term, char32_t wc, int width, bool insert_mode_disable) { xassert(width > 0); @@ -3918,7 +3918,8 @@ term_print(struct terminal *term, char32_t wc, int width) } print_linewrap(term); - print_insert(term, width); + if (!insert_mode_disable) + print_insert(term, width); int col = grid->cursor.point.col; @@ -3990,7 +3991,7 @@ term_print(struct terminal *term, char32_t wc, int width) static void ascii_printer_generic(struct terminal *term, char32_t wc) { - term_print(term, wc, 1); + term_print(term, wc, 1, false); } static void diff --git a/terminal.h b/terminal.h index 4242ed1d..d8e7cf94 100644 --- a/terminal.h +++ b/terminal.h @@ -894,7 +894,8 @@ void term_cursor_up(struct terminal *term, int count); void term_cursor_down(struct terminal *term, int count); void term_cursor_blink_update(struct terminal *term); -void term_print(struct terminal *term, char32_t wc, int width); +void term_print(struct terminal *term, char32_t wc, int width, + bool insert_mode_disable); void term_fill(struct terminal *term, int row, int col, uint8_t c, size_t count, bool use_sgr_attrs); diff --git a/vt.c b/vt.c index 2b5eb27d..bd1cf4ca 100644 --- a/vt.c +++ b/vt.c @@ -703,6 +703,7 @@ static void action_utf8_print(struct terminal *term, char32_t wc) { int width = c32width(wc); + bool insert_mode_disable = false; const bool grapheme_clustering = term->grapheme_shaping; #if !defined(FOOT_GRAPHEME_CLUSTERING) @@ -757,6 +758,7 @@ action_utf8_print(struct terminal *term, char32_t wc) if (base_width > 0) { term->grid->cursor.point.col = col; term->grid->cursor.lcf = false; + insert_mode_disable = true; if (composed == NULL) { bool base_from_primary; @@ -954,7 +956,7 @@ action_utf8_print(struct terminal *term, char32_t wc) out: if (width > 0) - term_print(term, wc, width); + term_print(term, wc, width, insert_mode_disable); } static void