aboutsummaryrefslogtreecommitdiffstats
path: root/tests
diff options
context:
space:
mode:
authorIngmar Steen <iksteen@gmail.com>2026-09-06 19:41:18 +0200
committerIngmar Steen <iksteen@gmail.com>2026-09-06 19:41:18 +0200
commit06b22770e0ff2d03dffafed223c63d7d4608020c (patch)
tree1e83190fa17ca21530502608e705923e8134cc07 /tests
parentserver: one synchronized message per frame, cells and images together (diff)
downloadslosh-06b22770e0ff2d03dffafed223c63d7d4608020c.tar.gz
slosh-06b22770e0ff2d03dffafed223c63d7d4608020c.tar.xz
slosh-06b22770e0ff2d03dffafed223c63d7d4608020c.zip
graphics: place an image once, not once per frame
Every frame re-emitted `a=p` for every visible placement. On a terminal that is invisible: a kitty placement is a standing instruction, not a per-frame one ("the other commands to erase text must have no effect on graphics"), so a repaint of the cells under a picture does not disturb it and saying it again says nothing new. It is not free anywhere else. A mirror cannot know that the identical placement is identical: shellglass re-decodes and re-encodes the image on every one, holds the placement back while that runs, and -- because an image change rides only in a full frame -- republishes the whole screen each time. Measured on one image and a pane printing text for twenty seconds, the mirror published 430 full frames and re-sent the image list 215 times; 215 of those fulls carried no image at all, because the placement was still mid-decode. That alternation is what was visible in the browser: a picture that flickered while anything on screen moved and vanished when it stopped. The same twenty seconds now costs one full frame, 412 diffs and one image list. So a placement is emitted only when it is new or has actually changed, and the client's state records what it is holding. Three things have to be right for that to be safe: a dropped frame un-marks the placements it carried (they are owed again, exactly as transmissions and deletions already were); a retransmit of the pixels invalidates the placement, because kitty deletes an image's placements when its data is replaced; and a full repaint clears the screen, which takes the images with it, so every placement is owed again -- gfx_repaint(), not gfx_reset(), because the pixels the terminal was sent survive a clear and a deletion still owed from a dropped frame is still owed. The live check drives a picture that does not move while the screen around it repaints: one placement across 121 frames, where the old behaviour emitted 120. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CRz3zwZJvBvzsR9k4cj6dd
Diffstat (limited to 'tests')
-rw-r--r--tests/live_graphics.py38
1 files changed, 36 insertions, 2 deletions
diff --git a/tests/live_graphics.py b/tests/live_graphics.py
index ce3a315..934cd46 100644
--- a/tests/live_graphics.py
+++ b/tests/live_graphics.py
@@ -58,12 +58,30 @@ time.sleep(30)
"""
-def spawn(cols=80, rows=24):
+# A small image that stays put while the screen around it changes: the counter
+# repaints one line in place (\r, no newline), so nothing scrolls and the
+# picture neither moves nor changes. Every frame it provokes is a frame that
+# must NOT say the placement again.
+STILL = r"""
+import base64, sys, time
+px = base64.b64encode(bytes([255, 0, 0] * 8)).decode() # 4x2 RGB
+sys.stdout.write("\x1b_Ga=T,f=24,q=2,s=4,v=2,i=7,c=6,r=2;%s\x1b\\\r\n" % px)
+sys.stdout.flush()
+for i in range(120):
+ sys.stdout.write("tick %d \r" % i)
+ sys.stdout.flush()
+ time.sleep(0.02)
+time.sleep(30)
+"""
+
+
+def spawn(cols=80, rows=24, child=None, session=None):
pid, fd = pty.fork()
if pid == 0:
os.environ["TERM"] = "xterm-ghostty"
os.environ["SLOSH_CONFIG"] = "/nonexistent/slosh.kdl"
- os.execv(BIN, [BIN, "-s", SESSION, "--", "python3", "-c", CHILD])
+ os.execv(BIN, [BIN, "-s", session or SESSION, "--", "python3", "-c",
+ child or CHILD])
os._exit(127)
fcntl.ioctl(fd, termios.TIOCSWINSZ, struct.pack("HHHH", rows, cols, 0, 0))
return pid, fd
@@ -201,6 +219,22 @@ def main():
os.waitpid(pid, os.WNOHANG) == (0, 0),
)
+ # A placement is a standing instruction, not a per-frame one: a picture
+ # that has not moved must not be re-sent while the screen around it
+ # repaints. Re-sending it is invisible on a terminal that keeps the pixels
+ # and ruinous on anything mirroring the stream, which re-decodes the image
+ # and rebuilds its view every time it hears the placement again.
+ pid3, fd3 = spawn(child=STILL, session=SESSION + "-still")
+ still = drain(fd3, idle=0.6, limit=20.0)
+ places = [ctl for ctl, _ in gfx_commands(still) if ctl.startswith(b"a=p")]
+ frames = still.count(b"\x1b[?2026h")
+ ok &= check(
+ "a still picture is placed once, however much repaints around it",
+ len(places) == 1 and frames > 10,
+ f"{len(places)} placements across {frames} frames",
+ )
+ os.kill(pid3, 9)
+
os.write(fd2, b"\x01q")
drain(fd2, idle=0.2, limit=2.0)
for child in (pid, pid2):