aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorIngmar Steen <iksteen@gmail.com>2026-09-06 19:41:56 +0200
committerIngmar Steen <iksteen@gmail.com>2026-09-06 19:41:56 +0200
commitb6fbc2184ff26425e212d88bbb3de04066a9e5f6 (patch)
tree49995f79ed0dcf9acca85cedf5cde0aa18e6e508
parentgraphics: place an image once, not once per frame (diff)
downloadslosh-b6fbc2184ff26425e212d88bbb3de04066a9e5f6.tar.gz
slosh-b6fbc2184ff26425e212d88bbb3de04066a9e5f6.tar.xz
slosh-b6fbc2184ff26425e212d88bbb3de04066a9e5f6.zip
graphics: state z=0 on a placement, so a mirror paints it over the text
kitty's default z is 0, which draws an image above the text, and that is the value slosh has always relied on -- it places after the cell diff precisely so the picture ends up on top. Saying nothing works because the terminal supplies the default. Anything mirroring the stream has no such default to fall back on. It cannot tell "the emitter wants this over the text" from "the emitter said nothing", and shellglass's viewer paints an unstated image under the cells. That is fine until a cell over the picture carries an explicit background -- and dim_unfocused gives one to every cell of a pane at once, so the moment focus moved elsewhere the whole image disappeared from the mirrored copy while the terminal, which took the default, still showed it. So the placement states z=0. Nothing changes on a terminal, since that is the value it was already assuming; the mirror now paints the picture over the cells and the dim pass runs underneath it, where it belongs. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CRz3zwZJvBvzsR9k4cj6dd
-rw-r--r--src/graphics.c10
-rw-r--r--tests/test_graphics.py21
2 files changed, 30 insertions, 1 deletions
diff --git a/src/graphics.c b/src/graphics.c
index 726e564..3330ece 100644
--- a/src/graphics.c
+++ b/src/graphics.c
@@ -342,7 +342,15 @@ char *gfx_flush(graphics_t *g, size_t *out_len) {
g->placed[g->nplaced++] = i;
p->shown = true;
out_fmt(g, "\x1b[%u;%uH", p->row + 1, p->col + 1);
- out_fmt(g, "\x1b_Ga=p,q=2,C=1,i=%u,p=%u", p->out_id, p->place_id);
+ /* z=0 is kitty's own default, so saying it changes nothing on a terminal
+ * -- and it is the whole picture for anything mirroring the stream, which
+ * cannot otherwise tell "the emitter wants this over the text" from "the
+ * emitter said nothing". Left unsaid, a mirror is free to paint the image
+ * under the cells, and the first pane-wide background to land on those
+ * cells (dim_unfocused paints one over every cell it touches) hides the
+ * picture completely -- on the mirrored copy only, while the terminal,
+ * which took the default, still shows it. */
+ out_fmt(g, "\x1b_Ga=p,q=2,C=1,z=0,i=%u,p=%u", p->out_id, p->place_id);
/* c=/r= mean *scale into this many cells*, so they are passed on only
* when the program asked for them. Sending the cell count a natural-size
* image happens to cover looks identical in a still picture and makes a
diff --git a/tests/test_graphics.py b/tests/test_graphics.py
index f8d7a0d..50edf43 100644
--- a/tests/test_graphics.py
+++ b/tests/test_graphics.py
@@ -289,6 +289,26 @@ def test_sub_cell_offsets_survive_to_the_client():
)
+def test_a_placement_says_it_sits_above_the_text():
+ """z=0 is kitty's default, so saying it changes nothing on a terminal.
+
+ It is the whole picture for anything mirroring the stream, which cannot
+ otherwise tell "the emitter wants this over the text" from "the emitter
+ said nothing" -- and one that guesses "under" loses the image behind the
+ first background painted over those cells, dim_unfocused being the one
+ that paints over every cell of a pane at once.
+ """
+ with Session(sends_image(), cols=44, rows=10) as s:
+ s.settle(200)
+ raw = s.api("graphics", format="bytes")["bytes"]
+ place = [c for c in raw.split("\x1b") if c.startswith("_Ga=p")]
+ check(
+ "the placement states its z",
+ place and "z=0" in place[0],
+ str(place),
+ )
+
+
def test_a_placement_with_no_offset_emits_none():
with Session(transmits_then_places("p,i=7,p=1,q=2,c=6,r=2"), cols=44, rows=10) as s:
s.settle(200)
@@ -661,6 +681,7 @@ if __name__ == "__main__":
test_an_image_outlives_a_screen_clear()
test_a_screen_clear_still_removes_what_is_on_screen()
test_sub_cell_offsets_survive_to_the_client()
+ test_a_placement_says_it_sits_above_the_text()
test_a_placement_with_no_offset_emits_none()
test_a_natural_image_is_never_rescaled_as_it_moves()
test_a_scaled_image_keeps_the_cell_count_it_asked_for()