aboutsummaryrefslogtreecommitdiffstats
path: root/web/build-site
diff options
context:
space:
mode:
authoruser <user@clank>2026-08-29 21:31:41 +0200
committeruser <user@clank>2026-08-29 21:31:41 +0200
commit8a8b5e1c7acd269d4a639a40712412a84480ad18 (patch)
treedd73377a2b0a8b424dc8f151ced00f702812edca /web/build-site
parentdocs: less wordy (diff)
downloadslosh-8a8b5e1c7acd269d4a639a40712412a84480ad18.tar.gz
slosh-8a8b5e1c7acd269d4a639a40712412a84480ad18.tar.xz
slosh-8a8b5e1c7acd269d4a639a40712412a84480ad18.zip
graphics: a frame the client never got must be said again
A pane image is re-emitted to the client as decoded RGBA, which for one screenshot is ~4MB of base64 in a single indivisible message -- and conn_send silently dropped anything that would overflow the 4MB outbox, while push_graphics ignored the refusal and the model advanced anyway. Everything stateful in that dropped frame was lost for good: the image was marked sent (so its placements drew nothing, forever), and the deletions rode out with it (so a placement the client had was never revoked). Reported as screenshots in a recovered pi transcript parked at a fixed spot on the screen, immune to scrolling and pane swaps -- those were placements whose deletion left with a dropped frame. The 1000x800 one could never be delivered at all: its transmission alone was bigger than the cap. So the frame is now transactional. Deletions queue and are repeated every flush until gfx_commit() confirms the client heard them; transmissions recorded in a frame are unwound by an undelivered commit so they are sent again. A flush nobody commits counts as undelivered at the next gfx_begin(), which is what the `graphics` control command is: bytes rendered for a CLI, not the client. The server reports the verdict and keeps the paint pending until it lands. The outbox cap now bounds the backlog rather than backlog-plus-message, so one big transmission on a drained queue always fits -- and that surfaced a second way to lose: a queued image legitimately holds the backlog over the cap until the client swallows it, and push_frame read that as a dead client and closed the connection. The session auto-detached every time a screenshot crossed the wire. A full outbox is a client that is *behind*, not gone: push_frame now marks the screen for a full repaint (the dropped diff means the client's cells are unknown) and retries as POLLOUT drains the queue; a client that truly left still shows up as a write error there. The headless suite pins the owed-until-committed contract; the new tests/live_graphics.py drives the real socket with a bigger-than-the-cap image and a reader that stalls, and would have caught both bugs: the flood must not detach, the transmission must arrive whole, and a placement scrolled away must be deleted -- then come back on scroll without a second copy of the pixels.
Diffstat (limited to 'web/build-site')
0 files changed, 0 insertions, 0 deletions