<feed xmlns='http://www.w3.org/2005/Atom'>
<title>slosh.git/src/osc5577.c, branch master</title>
<subtitle>an opinionated terminal multiplexer in C with programmable pane chrome</subtitle>
<id>https://git.sl0p.foo/slosh.git/atom/src/osc5577.c?h=master</id>
<link rel='self' href='https://git.sl0p.foo/slosh.git/atom/src/osc5577.c?h=master'/>
<link rel='alternate' type='text/html' href='https://git.sl0p.foo/slosh.git/'/>
<updated>2026-10-09T10:31:53Z</updated>
<entry>
<title>osc 7501: say what you are doing in somebody else's protocol</title>
<updated>2026-10-09T10:31:53Z</updated>
<author>
<name>user</name>
<email>user@clank</email>
</author>
<published>2026-10-09T10:24:48Z</published>
<link rel='alternate' type='text/html' href='https://git.sl0p.foo/slosh.git/commit/?id=fcf305085343bcb5c5240ed642468461f247f1ed'/>
<id>urn:sha1:fcf305085343bcb5c5240ed642468461f247f1ed</id>
<content type='text'>
OSC 5577 only works for programs that know slosh exists. The program
status protocol is the same idea written down for everybody, by ghostty's
author and implemented by ghostty and Rex, so a program that already
speaks it needs to know nothing about us:

  printf '\033]7501;state=working:app=cargo:msg=YnVpbGRpbmc=\033\\'

Implemented *as well as* 5577, not instead of it. 5577 is a two-way
control channel -- buttons answer on the program's stdin, shaders are
refused with a reason -- and 7501 deliberately is not, so neither absorbs
the other. What 7501 buys is the programs we do not control.

The scanner becomes one pass for four things: 5577, 7501, OSC 133 ; A and
RIS. The last two are here because 7501 ties record lifetime to them -- a
new prompt or a full reset is how a stale `working` dies without a
heartbeat -- and because a pane should pay for that scan once. osc5577_fn
gives way to osc_scan_cbs_t.

Reports are all-or-nothing, which is the spec's rule: no state, an unknown
state, a broken limit, base64 that will not decode, a control character in
decoded text or a malformed id discards the whole report rather than
writing a partial one. A malformed *pair* is skipped and the rest parsed.

A pane keeps up to PANE_PS_MAX records with LRU eviction, and composes the
one line it has from them by precedence -- blocked, error, done, working,
idle -- resolving `app` from the nearest ancestor that has one, the way
the hierarchy is allowed to mean something. It lands in the slot 5577's
`status` already had, because every reader of a pane wants "what is this
doing", not "which protocol said so"; `panes` reports program_state,
program_kind, program_app and progress beside it, with -1 for a progress
nobody reported, since a bar drawn at 0% says something false.

The feature-detection reply is rate-limited here. Note for the next
re-vendor: upstream lib-vt implements 7501 too, newer than our pin, and
answers the support query itself through write_pty without rate-limiting
it. See "What upstream did" under D1 in DESIGN.md before swapping this
half of the file for GHOSTTY_TERMINAL_OPT_PROGRAM_STATUS.
</content>
</entry>
<entry>
<title>sl0ppty: clang-format the tree</title>
<updated>2026-08-17T12:22:38Z</updated>
<author>
<name>user</name>
<email>user@clank</email>
</author>
<published>2026-08-17T12:22:38Z</published>
<link rel='alternate' type='text/html' href='https://git.sl0p.foo/slosh.git/commit/?id=ec91ccffe631ae34b111bf6161fd0e3c487f684c'/>
<id>urn:sha1:ec91ccffe631ae34b111bf6161fd0e3c487f684c</id>
<content type='text'>
The bumpy one. `make fmt` over every `.c` and `.h` outside `vendor/`: 32 files,
1,843 lines rewritten, no behaviour changed. Nothing here is worth reading -- the
next commit adds it to `.git-blame-ignore-revs` so `git blame` walks past it.

Two thirds of it is alignment the formatter now owns: trailing comments in a
struct, continuation lines under a signature. The rest is the hand-packed tables
opening up to one entry per line -- `ACTIONS`, `NAMED_KEYS`, `ACTION_HELP`. That
is a real loss of density and a real gain in diffs: adding an action was a reflow
of the block and is now one line. Anything worth keeping packed can be fenced with
`// clang-format off`; nothing was, on the grounds that a table is the thing a
formatter is for.

**One change is not formatting.** `src/config.h` had three trailing comments glued
onto one field:

    uint16_t hover_delay_ms; /* how long the pointer must rest to arm a guide */
    /* how long an announcement stays up */ /* rows per wheel notch */

-- `toast_ms`'s and `scroll_lines`' comments, landed on their neighbour by an
earlier edit, documenting the wrong things ever since. It is in this commit
because it is what caught it: clang-format could not settle on that line, wrapping
it one way and then re-indenting the wrap on a second pass, so `make fmt` was not
idempotent. The comments are back where they belong and it is.

Verified after the reformat, not before: `make clean &amp;&amp; make all` is
warning-free, `make retest` is 1801 checks green, and `make fmt` twice in a row
changes nothing the second time. A formatter that alters behaviour would be worse
than none, so the gate was the whole suite rather than a reading of the diff.
</content>
</entry>
<entry>
<title>sl0ptty: M4 — OSC 5577 pane status bars, buttons and click reports</title>
<updated>2026-08-13T21:07:27Z</updated>
<author>
<name>sl0p</name>
<email>sl0p@sl0p.foo</email>
</author>
<published>2026-08-13T21:07:27Z</published>
<link rel='alternate' type='text/html' href='https://git.sl0p.foo/slosh.git/commit/?id=105a6178542075ae212d396d10b7bd0fd5649f9c'/>
<id>urn:sha1:105a6178542075ae212d396d10b7bd0fd5649f9c</id>
<content type='text'>
The payoff milestone: sl0ppi's pi extensions work here unmodified.

  printf '\033]5577;1;status;building 3/7\033\\'
  printf '\033]5577;1;buttons;approve:Approve;cancel:Cancel\033\\'

  ╰ building 3/7 ──────────────────────[Approve]─[Cancel]─╯

libghostty-vt's UNKNOWN_SEQUENCE reports APC only, so the sequences are picked
up by a scanner (osc5577.c) reading the same bytes the terminal does — lib-vt
discards an OSC it does not know, so nothing is drawn and no pane output has
to be buffered. The scanner is a byte-at-a-time state machine, so a sequence
split across reads still lands.

Buttons are budgeted from the right before the status text gets any columns,
and each registers its hit as it is painted — the split button's rule, for the
split button's reason. A press sends the click report to that pane's stdin.

In-band purposes arrive here too, and by construction can never be declared: a
pane printing one is asking, not telling. A pane cannot relabel a purpose a
layout declared, which is now tested end to end rather than only at the API.

One real bug, found by the hostile-input tests: an over-long button id was
truncated into a *valid* one, so a pane could mint a 40-character id that
collides with the 32-character id its program already trusts. Ids are now
unescaped into a large buffer and validated at full length.

Acceptance test replays answer-picker's exact byte patterns, including
`buttons` with no payload (drop buttons, keep status) and a click report that
matches the extension's own CLICK_RE.

Four test bugs on the way, each worth remembering: printf eats %3B as a format
specifier; the driver's unescaper reads \0 as NUL so \033 became NUL+'33'; a
trailing ST inside a double-quoted shell string escapes the quote (BEL avoids
it); and readline eats a typed ESC as a meta prefix, so tests that drive a
pane must not use an interactive shell.

187 checks green (160 headless + 27 live).
</content>
</entry>
</feed>
