aboutsummaryrefslogtreecommitdiffstats
path: root/src/config.h
diff options
context:
space:
mode:
authoruser <user@clank>2026-08-15 18:44:07 +0200
committeruser <user@clank>2026-08-15 18:44:07 +0200
commit93ce0b645babd0851edb34701bbfffa5565daf65 (patch)
treeb402f934e5624a731fef4988d5d4e640766817fa /src/config.h
parentsl0ppty: watch the config directory even when it is not there yet (diff)
downloadslosh-93ce0b645babd0851edb34701bbfffa5565daf65.tar.gz
slosh-93ce0b645babd0851edb34701bbfffa5565daf65.tar.xz
slosh-93ce0b645babd0851edb34701bbfffa5565daf65.zip
sl0ppty: measure chrome in columns, not characters
Good catch: a two-column bell mark shifted the pane title row. Everything in app.c that positions chrome -- title budgets, button placement, the hit rects that have to agree with them -- came from a `cells()` that counted codepoints, and a glyph drawn in two columns was booked as one, so every glyph after it landed a column early. Widths now come from lib-vt's own table, via ghostty_unicode_grapheme_width. That is deliberately the same table the terminal uses for pane content, so chrome and content cannot disagree about how wide something is, and it is cluster-aware: an emoji with a variation selector is two codepoints and one glyph. A wide cell claims its right half as a tail (width 0), which the renderer already knew how to skip. HANDOVER said this was a known trap and to dump the actual cells before touching arithmetic, which is exactly how this went: the first assertion I wrote passed against a deliberately broken width function, because it measured the frame's right corner -- drawn at an absolute position, so it does not move -- rather than the columns the mark claims. The test now reads the mark's own colour runs and demands 1 column for a narrow mark and 2 for a wide one, and it fails when the width function is stubbed back to 1. Two things fell out on the way: ghostty_unicode_grapheme_width measures only the *first* cluster and returns the codepoints it consumed. My first loop ignored that and asked "did the width change when I added the next codepoint?", which concludes that an entire ASCII string is a single cluster -- the cheatsheet modal collapsed to ten columns wide with the text clipped. Use the return value. A cluster can also be longer than a cell holds: a ZWJ family emoji is 18 bytes against 16. Both the cell writer and the config's mark fields (now 32 bytes) clamp on a codepoint boundary, because snprintf cuts at a byte and half a codepoint is invalid UTF-8 -- which showed up as a decoder error in a test far from the cause, not as a short mark.
Diffstat (limited to 'src/config.h')
-rw-r--r--src/config.h9
1 files changed, 6 insertions, 3 deletions
diff --git a/src/config.h b/src/config.h
index 8c01fdb..36e9915 100644
--- a/src/config.h
+++ b/src/config.h
@@ -110,16 +110,19 @@ typedef struct {
* reads as even: a glyph that floats in a mostly-empty cell donates its own
* whitespace to the gap beside it and looks further away than its
* neighbours, which is what an en dash did here. */
- char zoom_mark[16], zoom_on_mark[16], close_mark[16], min_mark[16];
+ /* 32 bytes because a mark is a grapheme cluster, not a character: a
+ family emoji is eighteen bytes on its own. Truncating one mid-
+ codepoint used to put invalid UTF-8 on the wire. */
+ char zoom_mark[32], zoom_on_mark[32], close_mark[32], min_mark[32];
/* The new-tab button at the end of the strip. Drawn with a space each side,
* so a one-character mark is still a three-cell target. */
- char newtab_mark[16];
+ char newtab_mark[32];
bool bell_indicator;
/* What to draw. A short string rather than a fixed glyph: the obvious
* choice is an emoji bell, and emoji are drawn two columns wide by many
* terminals while chrome here is booked as one — so the default is narrow
* and anyone who knows their terminal can say otherwise. */
- char bell_mark[16];
+ char bell_mark[32];
/* Which dead panes stay, showing what they printed and offering to run
* again, and which just go.
*