<feed xmlns='http://www.w3.org/2005/Atom'>
<title>slosh.git/src/input.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/input.c?h=master</id>
<link rel='self' href='https://git.sl0p.foo/slosh.git/atom/src/input.c?h=master'/>
<link rel='alternate' type='text/html' href='https://git.sl0p.foo/slosh.git/'/>
<updated>2026-09-08T14:58:24Z</updated>
<entry>
<title>search: a scrollback search bar on /, matches highlighted</title>
<updated>2026-09-08T14:58:24Z</updated>
<author>
<name>user</name>
<email>user@clank</email>
</author>
<published>2026-09-08T14:58:24Z</published>
<link rel='alternate' type='text/html' href='https://git.sl0p.foo/slosh.git/commit/?id=935fb7e93726dcb8ab0529c2dbcb6b5e75656794'/>
<id>urn:sha1:935fb7e93726dcb8ab0529c2dbcb6b5e75656794</id>
<content type='text'>
C-a / opens an inline search bar on the focused pane's status row, on
lib-vt's GhosttySearch (D4's pattern: the scrollback walk, reflow and
alt-screen survival are the library's, the UI and the wiring are ours).
Type to search live; the viewport follows the current match; up/down
(or C-n/C-p) step through them with wraparound; Enter keeps your place,
Escape snaps back. A k-of-n counter reads on the right, or "no matches".

Every match on screen is tinted, and the one the bar is on is tinted
apart -- warm gold by default, painted after the shader pass so the
scrolled wash does not dilute a match you are hunting for. Four themeable
colours (search_fg/bg, search_cur_fg/bg).

Bare / took the key from help, which keeps ?; that needed the legacy
decoder to read the shift out of a punctuation byte the way it already
did for capitals -- without it ? and / decode identically (SLASH, no
mods) and cannot be two keys on a client without the kitty protocol.

tests/test_search.py drives it end to end (39 checks): the bar, the
counter, stepping and wrap, all-match vs current highlighting, Enter/
Escape, new output updating the count, case-insensitivity, the keyboard
belonging to the bar, click-away, and that / and ? no longer collide.
</content>
</entry>
<entry>
<title>parsers: fix seven bugs found by fuzzing</title>
<updated>2026-08-19T20:48:31Z</updated>
<author>
<name>user</name>
<email>user@clank</email>
</author>
<published>2026-08-19T20:48:31Z</published>
<link rel='alternate' type='text/html' href='https://git.sl0p.foo/slosh.git/commit/?id=009350acbcd4360680b8fa228d8cd03a2f2b5e93'/>
<id>urn:sha1:009350acbcd4360680b8fa228d8cd03a2f2b5e93</id>
<content type='text'>
kdl: heap-buffer-overflow on trailing backslash in a string (a"\)
jsonval: heap-buffer-overflow on truncated \uXXXX escape ("\u)
jsonval: UB float-to-long cast on out-of-range numbers (2e22)
expr: UB signed overflow in arithmetic ops — wrap as uint32
expr: INT32_MIN / -1 SIGFPE guard in OP_DIV/OP_MOD
expr: stack overflow on deep nesting — add MAX_DEPTH 256
expr: 64-bit + saturation in OP_DIST to prevent squared overflow
input: NULL memcpy in input_timeout's re-parse path
input: signed overflow in CSI and SGR mouse parameter accumulation
</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>sl0ppty: make the finder a modal, and a picker worth using</title>
<updated>2026-08-15T18:10:27Z</updated>
<author>
<name>user</name>
<email>user@clank</email>
</author>
<published>2026-08-15T18:10:27Z</published>
<link rel='alternate' type='text/html' href='https://git.sl0p.foo/slosh.git/commit/?id=bd84065613df44f131476d4b66fd39b2c76da801'/>
<id>urn:sha1:bd84065613df44f131476d4b66fd39b2c76da801</id>
<content type='text'>
It was a bare coloured rectangle painted over whatever pane it landed
on: no border, no title, no scrim, no way out you could see. It read as
output from the program underneath rather than as a window in front of
it, which is the one thing an overlay has to get right. It is now a
modal like the cheatsheet -- same frame, same scrim, same close button
in the place a pane keeps one -- differing in the way that matters: the
cheatsheet is read, so any key dismisses it, while this one is used, so
it keeps the keyboard until it is answered.

The list was the other half of the problem. It showed `1:` for a tab
with no name, which looks like something failed to load; a row for a
pane with no purpose was three columns of nothing; there was no mark for
the pane you are already in, no count, and nothing to say a list had
more below the tenth row -- so a list that scrolled looked like a list
that ended. An empty result was an empty box, which reads as a broken
finder rather than a wrong query.

Now: a query field that looks like one (so it shows where the typing
goes before you type), the current pane marked, tab/title/purpose told
apart by weight, the row under the pointer lit, "N of M" in the footer,
arrows for what is off each end, and "no pane matches that" when there
is nothing.

Keys, because a picker in this shape is reached for by hands that
already know one: C-n/C-p, tab/shift-tab, home/end, page up/down, C-u to
clear the query, and wrapping at both ends -- one press of Up for the
last entry is worth more in a short list than overshoot protection.
Backspace deleted a *byte*, so one press through a multi-byte character
left the rest of it in the query; it deletes a character now, the way
the rename editor already did.

The finder owns the pointer as well as the keyboard: a press that is not
on one of its rows dismisses it and stops there, rather than also
landing on the layout behind and focusing the pane you were clicking
away from.

Found on the way: CSI Z (shift+tab) was not parsed at all, so it reached
neither a binding nor the program in the pane -- every terminal sends
back-tab that way, and it is the one chord whose shift lives in the
final byte rather than in a modifier param.
</content>
</entry>
<entry>
<title>sl0ptty: input decoder + per-pane re-encode</title>
<updated>2026-08-13T20:08:30Z</updated>
<author>
<name>sl0p</name>
<email>sl0p@sl0p.foo</email>
</author>
<published>2026-08-13T20:08:30Z</published>
<link rel='alternate' type='text/html' href='https://git.sl0p.foo/slosh.git/commit/?id=7521f217a5ed98cef16db5837a72fae32dce9552'/>
<id>urn:sha1:7521f217a5ed98cef16db5837a72fae32dce9552</id>
<content type='text'>
The half libghostty-vt does not do: decode what the outer terminal sends us
into semantic events, then re-encode each event for the target pane against
that pane's own negotiated modes.

Decoder (src/input.c): legacy keys and modifiers, SS3, tilde keys, the kitty
keyboard protocol (CSI u, shifted/base layout sub-params, press/repeat/release,
associated text), SGR mouse incl. drag and wheel, bracketed paste, focus
events, alt-prefix, UTF-8, and partial sequences retained across reads.

Re-encoding (pane_send_key/mouse/paste) drives lib-vt's key and mouse encoders
via setopt_from_terminal, so a pane gets exactly what it negotiated. The mouse
encoder wants surface pixels; we declare a 1x1 pixel cell so cell coordinates
are exact rather than approximated.

The prefix key is handled on decoded events, not bytes, so it cannot be
confused by a byte that merely looks like C-a inside a UTF-8 or escape
sequence.

Tests: 40 table-driven decoder cases (tests/input_test.c) + a live check that
drives the same client into a legacy pane and a kitty/mouse pane and asserts
each receives its own encoding (tests/live_input.py). 59 checks, all green.
</content>
</entry>
</feed>
