diff options
| author | user <user@clank> | 2026-08-16 00:52:42 +0200 |
|---|---|---|
| committer | user <user@clank> | 2026-08-16 00:52:42 +0200 |
| commit | 40edef803e1b162ddc259e74aadd3688631ce1f2 (patch) | |
| tree | db86f33266d3f3e8085a5b9b69da8b3c4231b3f2 /src | |
| parent | sl0ppty: one shader entry, one parser (diff) | |
| download | slosh-40edef803e1b162ddc259e74aadd3688631ce1f2.tar.gz slosh-40edef803e1b162ddc259e74aadd3688631ce1f2.tar.xz slosh-40edef803e1b162ddc259e74aadd3688631ce1f2.zip | |
sl0ppty: a reply is never a request
`hello` was answered with the verb `hello`. Any pane that echoes what it is
sent -- `cat`, `tee`, a shell with echo on, a REPL waiting for a line --
therefore answered the answer, and the session answered that. Measured
before the fix: a pane running `tee` was sent 4,303,832 bytes and 165,532
hellos in a second and a half. Afterwards, 32 bytes and one.
The fix is the rule rather than the instance: every message the session
sends *to* a program ends its verb in `-reply`, no request verb may, and
`on_osc5577` drops one on sight instead of relying on no handler happening
to exist. So this cannot come back when the next verb gets an answer --
which is the commit after this one, and how I found it.
`hello`'s reply is now `hello-reply;sl0ppty;1`. That is our own extension
to the fork's protocol, not one of the five compatible verbs, so nothing
written against sl0ppi notices. The fork's `click` goes to a program too
and keeps its name: it predates the rule and is safe for the weaker reason
that nothing answers a click.
The suite was hiding this. `emit()` builds its pane out of `cat -v`, so the
existing `hello` test had a pane trading megabytes with the session while
it asserted -- and passed, because the assertion was `in`, and the first
reply was in there along with all the rest. It now counts.
Carries `pane_touch`, two lines, unused until the next commit: a pane whose
shaders changed has to be repainted, and nothing about its cells changed to
say so.
Diffstat (limited to 'src')
| -rw-r--r-- | src/osc5577.h | 7 | ||||
| -rw-r--r-- | src/pane.c | 20 | ||||
| -rw-r--r-- | src/sl0ppty.h | 3 |
3 files changed, 29 insertions, 1 deletions
diff --git a/src/osc5577.h b/src/osc5577.h index 7170c37..21b82a1 100644 --- a/src/osc5577.h +++ b/src/osc5577.h @@ -11,6 +11,13 @@ * * \033]5577;1;click;approve\033\\ * + * A reply is never a request. Everything the session sends back to a program + * ends its verb in `-reply` (`hello-reply`, `shader-reply`), and no request verb + * may, because a pane that echoes what it is sent -- `cat`, a shell with echo + * on, a REPL waiting for a line -- would otherwise be answered into a loop. The + * fork's `click` predates the rule and is safe for the weaker reason that + * nothing answers a click. + * * libghostty-vt's UNKNOWN_SEQUENCE effect reports APC only, not unknown OSC, * so we scan the pty stream ourselves. The bytes still go to lib-vt, which * discards an OSC it does not know — nothing is drawn, and we need no @@ -130,6 +130,19 @@ static void parse_buttons(pane_t *p, const char *payload) { static void on_osc5577(const char *verb, const char *payload, void *ud) { pane_t *p = ud; + + /* A reply is never a request. Everything the session sends *to* a program ends + * its verb in `-reply`, and nothing here dispatches one, so a pane that echoes + * what it is sent -- `cat`, `tee`, a shell with echo on, a REPL waiting for a + * line -- cannot be answered into a loop. + * + * Found the hard way: `hello` used to answer with the verb `hello`, and a pane + * running `tee` traded four megabytes of them with the session in a second and + * a half. The fork's `click` (D1) goes to a program too and predates the rule; + * it is safe because nothing answers a click. */ + size_t vlen = strlen(verb); + if (vlen >= 6 && strcmp(verb + vlen - 6, "-reply") == 0) return; + if (strcmp(verb, "status") == 0) { snprintf(p->status, sizeof p->status, "%s", payload); p->dirty = true; @@ -142,7 +155,7 @@ static void on_osc5577(const char *verb, const char *payload, void *ud) { } else if (strcmp(verb, "hello") == 0) { /* Our addition to the fork's protocol, which is unversioned in practice: * a program can ask what it is talking to before using anything new. */ - const char *reply = "\033]5577;1;hello;sl0ppty;1\033\\"; + const char *reply = "\033]5577;1;hello-reply;sl0ppty;1\033\\"; pane_write(p, reply, strlen(reply)); } else if (p->osc_cb) { p->osc_cb(p, verb, payload, p->osc_ud); /* purpose, and anything later */ @@ -400,6 +413,11 @@ void pane_clear_bell(pane_t *p) { p->dirty = true; } +/* Nothing about the pane's cells changed, but what is drawn over them did. + * Composing is the only thing that can notice a new shader chain, and the loop + * only composes when something is dirty. */ +void pane_touch(pane_t *p) { p->dirty = true; } + void pane_set_name(pane_t *p, const char *name) { if (!name) name = ""; snprintf(p->name, sizeof p->name, "%s", name); diff --git a/src/sl0ppty.h b/src/sl0ppty.h index 319fcba..037a050 100644 --- a/src/sl0ppty.h +++ b/src/sl0ppty.h @@ -179,6 +179,9 @@ void pane_resize(pane_t *p, uint16_t cols, uint16_t rows); * is dropped, which is a picture that silently does not appear. */ void pane_set_cell_px(pane_t *p, uint16_t w, uint16_t h); bool pane_dirty(pane_t *p); +/* Ask for a repaint without having changed a cell: what is drawn *over* the + * pane changed, and composing is the only thing that can see that. */ +void pane_touch(pane_t *p); /* Scrollback. Negative delta scrolls up (towards older output). */ void pane_scroll(pane_t *p, int delta); void pane_scroll_edge(pane_t *p, bool top); |
