From cc87db74c78c28a6f9c6619ceae3ae97a0b95c53 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 9 Jun 2026 11:49:36 +0000 Subject: [PATCH] Two fixes for RI codepoints. Firstly, do not combine more than two of them - previously we were ending up with four codepoints in one cell which tmux believed to be width 2, but terminals considered width 4. Secondly, invalidate cursor position before redrawing the cell when the second codepoint is received, terminals vary in how they manage backspace and cursor movement across these characters, so it is better to use absolute rather than relative positioning. GitHub issue 4853. --- tty.c | 2 ++ utf8-combined.c | 24 +++++++++++++++++++++++- 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/tty.c b/tty.c index f201ba1d..5fd29130 100644 --- a/tty.c +++ b/tty.c @@ -2051,6 +2051,8 @@ tty_cmd_cell(struct tty *tty, const struct tty_ctx *ctx) tty_region_pane(tty, ctx, ctx->orupper, ctx->orlower); tty_margin_off(tty); + if (ctx->flags & TTY_CTX_CELL_INVALIDATE) + tty_invalidate(tty); tty_cursor_pane_unless_wrap(tty, ctx, ctx->ocx, ctx->ocy); tty_cell(tty, ctx->cell, &ctx->defaults, ctx->palette, diff --git a/utf8-combined.c b/utf8-combined.c index 65ecf9cd..923342b4 100644 --- a/utf8-combined.c +++ b/utf8-combined.c @@ -82,6 +82,23 @@ utf8_is_hangul_filler(const struct utf8_data *ud) return (memcmp(ud->data, "\343\205\244", 3) == 0); } +/* Count regional indicator characters. */ +static u_int +utf8_regional_count(const struct utf8_data *ud) +{ + u_int count = 0, i; + + for (i = 0; i + 4 <= ud->size; i++) { + if (ud->data[i] == 0xf0 && + ud->data[i + 1] == 0x9f && + ud->data[i + 2] == 0x87 && + ud->data[i + 3] >= 0xa6 && + ud->data[i + 3] <= 0xbf) + count++; + } + return (count); +} + /* Should these two characters combine? */ int utf8_should_combine(const struct utf8_data *with, const struct utf8_data *add) @@ -94,8 +111,13 @@ utf8_should_combine(const struct utf8_data *with, const struct utf8_data *add) return (0); /* Regional indicators. */ - if ((a >= 0x1F1E6 && a <= 0x1F1FF) && (w >= 0x1F1E6 && w <= 0x1F1FF)) + if ((a >= 0x1F1E6 && a <= 0x1F1FF) && (w >= 0x1F1E6 && w <= 0x1F1FF)) { + if (utf8_regional_count(with) != 1) + return (0); + if (utf8_regional_count(add) != 1) + return (0); return (1); + } /* Emoji skin tone modifiers. */ switch (a) {