<feed xmlns='http://www.w3.org/2005/Atom'>
<title>slosh.git/tests, branch v0.1.0</title>
<subtitle>an opinionated terminal multiplexer in C with programmable pane chrome</subtitle>
<id>https://git.sl0p.foo/slosh.git/atom/tests?h=v0.1.0</id>
<link rel='self' href='https://git.sl0p.foo/slosh.git/atom/tests?h=v0.1.0'/>
<link rel='alternate' type='text/html' href='https://git.sl0p.foo/slosh.git/'/>
<updated>2026-08-27T13:27:51Z</updated>
<entry>
<title>slosh(5): the file formats get their manpage; slosh(1) learns shaders</title>
<updated>2026-08-27T13:27:51Z</updated>
<author>
<name>user</name>
<email>user@clank</email>
</author>
<published>2026-08-27T13:27:51Z</published>
<link rel='alternate' type='text/html' href='https://git.sl0p.foo/slosh.git/commit/?id=b5df6a7b1d73fc9740a077e8eca2d7fc8f6afe60'/>
<id>urn:sha1:b5df6a7b1d73fc9740a077e8eca2d7fc8f6afe60</id>
<content type='text'>
Section 5 is where unix keeps its file formats, and slosh has two
documents sharing one syntax -- the configuration and the layouts -- so
they share a page the way they share a parser. docs/slosh.5 covers the
KDL subset, where the file lives and how reload refuses a broken one,
include and its precedence, every top-level setting grouped the way
config.md groups them, the theme block, the shader vocabulary with the
expression language summarised, the states and their ranking, the keys
block including direct bindings, and the layout schema (tab, pane, and
their properties).

Held to the source like slosh.1 before it: every name in config.c's
KNOWN_TOP -- the list the loader refuses unknown settings against,
itself held to the loader by test_config.py -- must appear in the page,
so a knob cannot be added without this page hearing about it. The pair
must cross-reference each other, and both must be pure ASCII and lint
clean under groff -ww.

slosh(1) grows a SHADERS section -- the one feature a synopsis reader
would never guess a multiplexer had -- and its SEE ALSO now names
slosh(5) and nothing else: the other multiplexers can advertise
themselves.

Install puts the page under share/man/man5, the release tarballs carry
both pages, and the Homebrew formula installs what the commit it is
building has, guarded as before.
</content>
</entry>
<entry>
<title>a manpage: slosh(1), installed, shipped, and held to the source</title>
<updated>2026-08-27T13:17:57Z</updated>
<author>
<name>user</name>
<email>user@clank</email>
</author>
<published>2026-08-27T13:17:57Z</published>
<link rel='alternate' type='text/html' href='https://git.sl0p.foo/slosh.git/commit/?id=ee5790225261fb3d473a16a365bedc15619f93cc'/>
<id>urn:sha1:ee5790225261fb3d473a16a365bedc15619f93cc</id>
<content type='text'>
The distributables were a bare binary and a directory of Markdown, which
is fine until somebody types 'man slosh' -- the unix-traditional place
to ask. docs/slosh.1 is hand-written man(7), pure ASCII roff, no
toolchain: the synopsis, the options, the session commands, the default
keys, the mouse (including the new drag-past-an-edge scrolling), where
the configuration lives, what SLOSH_* means in a pane's environment,
files, exit status. The prose docs stay the long form; the page says so
and points at them.

'make install' puts it under PREFIX/share/man/man1 (and uninstall takes
it away), the release tarballs carry it beside the LICENSE -- a tarball
is what a packager starts from -- and the Homebrew formula installs it
when the commit it is building has one, guarded because the formula must
be able to build commits older than the page.

Prose about a program drifts unless something holds them together, so
test_docs.py holds the page to the source the way it already holds the
site to docs/nav: every option main.c's parser compares against and
every word command must appear in the page, every SLOSH_* variable the
code reads or sets must be documented, the roff must be pure ASCII (it
travels through implementations that never heard of preconv), and where
groff exists the page must lint clean at CHECKSTYLE=3 with -ww. Adding
a flag without documenting it is now a failing test, which is the only
kind of documentation policy that survives contact with a deadline.
</content>
</entry>
<entry>
<title>test_splash: 'landed' is the row in its final form</title>
<updated>2026-08-27T13:17:48Z</updated>
<author>
<name>user</name>
<email>user@clank</email>
</author>
<published>2026-08-27T13:17:48Z</published>
<link rel='alternate' type='text/html' href='https://git.sl0p.foo/slosh.git/commit/?id=7ceb4e1492922fc33d0f9d13d01eec72bfda2363'/>
<id>urn:sha1:7ceb4e1492922fc33d0f9d13d01eec72bfda2363</id>
<content type='text'>
The h-centered check raced the assembly it was measuring. landed_row()
answered as soon as one occurrence of the opening run had a block glyph
at its start -- but the logo's first line contains that run three times,
assembly is staggered, and under a parallel suite's load the middle
section can finish whole checks before the leftmost. The test then
measured a middle occurrence's x (24) and called the logo off-centre.

The row is 'landed' when all three occurrences are present -- its final
form -- and the first block glyph is still required to sit where the
match begins, so a particle passing to the left keeps us waiting rather
than shifting the answer.
</content>
</entry>
<entry>
<title>selection: drag past an edge scrolls the viewport under it</title>
<updated>2026-08-27T13:09:02Z</updated>
<author>
<name>user</name>
<email>user@clank</email>
</author>
<published>2026-08-27T13:09:02Z</published>
<link rel='alternate' type='text/html' href='https://git.sl0p.foo/slosh.git/commit/?id=2ee881452aa4b19701a071f0333a15265e3b314e'/>
<id>urn:sha1:2ee881452aa4b19701a071f0333a15265e3b314e</id>
<content type='text'>
Text taller than the viewport was not selectable in one gesture: the
drag stopped dead at the pane's border. Two faults, one visible and one
waiting. The visible one: DRAG_SELECT motion subtracted the content
origin in uint16_t, so a pointer above or left of the pane underflowed,
the grid lookup failed, and the selection froze. The waiting one: the
anchor was a pair of *viewport* coordinates re-resolved on every extend,
so the moment anything scrolled mid-drag the anchor would have drifted
with the viewport, re-anchoring to whatever slid under the press.

The anchor is now a tracked grid ref -- the thing lib-vt documents as
being for 'long-lived anchors such as selections' -- so it follows its
cell across scrolling, pruning and reflow, and only the near end of the
selection moves. If a program flooding output prunes the anchored row
out of scrollback entirely, the selection keeps what it had; there is no
cell left to anchor a new answer to.

Motion clamps into the content rect (a pointer past an edge is pointing
at the nearest cell), and holding past the top or bottom edge scrolls on
a clock rather than on motion, because a resting pointer sends no events
and 'keep going' is exactly what resting it there means. app_tick() does
the step, app_next_deadline_ms() wakes the poll loop for it -- the same
mechanism toasts and hover guides already use -- and both front ends
call the tick, so the behaviour exists under the server and the headless
harness alike. Rows per step is how far past the edge the pointer sits:
one row past creeps, further hurries, so the hand already dragging is
also the throttle. The metronome is select_scroll_ms, a knob so the test
can configure the clock down instead of sleeping.

The test drags to one row above the pane and simply holds: the viewport
walks to the top of scrollback (clamping makes the endpoint exact), the
copy starts at a line that was never on screen with the press, and still
ends at the pressed row -- that last check is the tracked anchor's.
</content>
</entry>
<entry>
<title>tests: the greeting is not what the reattach test is about</title>
<updated>2026-08-26T14:48:17Z</updated>
<author>
<name>Peter Geissler</name>
<email>peter@haxx.in</email>
</author>
<published>2026-08-26T14:44:40Z</published>
<link rel='alternate' type='text/html' href='https://git.sl0p.foo/slosh.git/commit/?id=4c1f7f8ebd47ac7906ba9629fc67b53fd317fb5f'/>
<id>urn:sha1:4c1f7f8ebd47ac7906ba9629fc67b53fd317fb5f</id>
<content type='text'>
"reattaching repaints the whole screen" greps the client's byte stream
for the text a pane was holding, and failed about half the time.

The splash assembles from particles scattered over the entire screen, so
for the first few hundred milliseconds of an attach any cell can be
holding a fragment of the logo. The full repaint that a reattach
triggers goes out during exactly that window, so it can carry

    │marker-alp▄a

with a particle where the "h" should be. What restores that cell when
the greeting ends is a one-cell diff, cursor-addressed, so the string is
never contiguous in the stream again and the substring check fails.

Nothing about slosh is wrong here -- the model has the text throughout,
and `snapshot text` shows it -- so the fix is to stop asking this file to
assert about the bytes of an animation it is not testing. The session
under test gets a config with splash_ms 0. The greeting has test_splash.

5/5 where it was roughly half.
</content>
</entry>
<entry>
<title>macos: the kernel still knows, it just spells /proc differently</title>
<updated>2026-08-26T14:11:42Z</updated>
<author>
<name>user</name>
<email>user@clank</email>
</author>
<published>2026-08-26T14:11:42Z</published>
<link rel='alternate' type='text/html' href='https://git.sl0p.foo/slosh.git/commit/?id=087b2bd1d31dac24f8b0ffe79c33f4ebf1f60bd3'/>
<id>urn:sha1:087b2bd1d31dac24f8b0ffe79c33f4ebf1f60bd3</id>
<content type='text'>
Portability fixes contributed by our macOS developer, applied with one
hardening tweak (below).

Darwin has no /proc, so everything that read one gets the native answer:
live_cwd via proc_pidinfo(PROC_PIDVNODEPATHINFO), the foreground command
via sysctl(KERN_PROCARGS2) parsed back into the NUL-separated shape
/proc/&lt;pid&gt;/cmdline gives, and SLOSH_BIN via _NSGetExecutablePath plus
realpath. The pty gets its winsize set on the slave after opening it,
because Darwin attaches no line discipline to the master until then and
the earlier TIOCSWINSZ was silently dropped -- a pane born 0x0. The
shader repl binds tab in libedit's own words, since macOS ships Python's
readline as a libedit wrapper that accepts GNU syntax in silence.

Two finds are cross-platform bugs: a toast wider than the screen minus
the gap computed a negative x that uint16_t made ~65533, drawing the
longest messages -- the ones most worth reading -- off screen; now the
middle is elided (a path's tail is the half you needed) and x clamps.
And a pane label was 128 bytes, which a Homebrew interpreter's full path
exceeds before its arguments even start; dump-layout wrote the
truncation down and a restore ran it, so it is 1024 now.

Saving a layout learned cwd_as_named: the kernel answers with the
physical path, and on macOS /tmp is a symlink to /private/tmp, so a
layout that said cwd="/tmp" stopped round-tripping through a save.
If realpath(named) is where the pane still is, the named spelling wins.

Tests follow: realpath'd tmpdirs, the scroll test reads the shell's
first prompt instead of spelling out bash 5.3, and wrapped pane text is
joined before matching a long path.

One tweak on top of the contribution: the elision needs five columns
for a head, the dots and a tail -- below that `avail - 3` goes
negative, and negative as size_t is a pointer into the weeds; a
terminal that narrow gets a plain clip.
</content>
</entry>
<entry>
<title>compact: shared borders instead of gaps (D23)</title>
<updated>2026-08-25T12:23:33Z</updated>
<author>
<name>user</name>
<email>user@clank</email>
</author>
<published>2026-08-25T12:23:33Z</published>
<link rel='alternate' type='text/html' href='https://git.sl0p.foo/slosh.git/commit/?id=b744a90859bf248bfbea97f990668301b5fa9537'/>
<id>urn:sha1:b744a90859bf248bfbea97f990668301b5fa9537</id>
<content type='text'>
One knob, `compact true`: panes pack flush against 1-cell divider lines
that meet in real junctions, one frame rings the tab, and a pane's title
rides the line above it -- a boundary costs one cell where the default
look spends four. The lines are strokes: each write says which edges of
its cell it needs, unions with what is there, and never clobbers text,
so ├ ┬ ┼ fall out of paint order and the focus ring recolours without
inventing arms. The divider *is* the gap (eff_gap, one answer for the
layout, split_fits and the resize clamp), so gap hits, hover hints and
corner drags work unchanged. Interior edges give up click-to-split --
one cell cannot hold two verbs -- and a pane's status row overlays its
last content row. Floats, zoom and the D6 flatten keep classic frames
through place_hidden: zero new tree cases.

Underneath it, mode or no mode: a border drag now moves the boundary on
that side, converted on the first motion so the press that never moves
keeps its old meaning -- the handle's click still splits, the rim's is
still deliberately nothing (drag.rim). Which is also the fix for gap 0,
where flush panes leave no gap cell to grab and the resize gesture had
silently vanished with it.
</content>
</entry>
<entry>
<title>slosh: a pane arriving in a zoomed tab ends the zoom</title>
<updated>2026-08-22T16:59:41Z</updated>
<author>
<name>user</name>
<email>user@clank</email>
</author>
<published>2026-08-22T16:59:41Z</published>
<link rel='alternate' type='text/html' href='https://git.sl0p.foo/slosh.git/commit/?id=4d2cd060089a4c48c24b93253f49bfd5bf22d53b'/>
<id>urn:sha1:4d2cd060089a4c48c24b93253f49bfd5bf22d53b</id>
<content type='text'>
A split, the config editor or a new float while zoomed used to land the
new pane behind the pane filling the tab -- which looks exactly like the
verb having failed. Whoever asked for a new pane asked to see it, so the
zoom yields, in place_beside where every such arrival funnels through;
the move-pane unzoom was this rule as a special case and folds in.
Minimising the zoomed pane ends the zoom too, or the layout would solo a
pane the strip now holds.
</content>
</entry>
<entry>
<title>slosh: refuse to attach to the session this pane is inside</title>
<updated>2026-08-22T16:30:19Z</updated>
<author>
<name>user</name>
<email>user@clank</email>
</author>
<published>2026-08-22T16:30:19Z</published>
<link rel='alternate' type='text/html' href='https://git.sl0p.foo/slosh.git/commit/?id=063d43f7702fa74071e8555c6e9dabba1a914c74'/>
<id>urn:sha1:063d43f7702fa74071e8555c6e9dabba1a914c74</id>
<content type='text'>
A new client displaces the current display, so `slosh` typed into a pane
of "main" displaced the very client showing that pane -- you watched your
own session detach, from inside it. SLOSH_SESSION names the pane's own
session; that one name is refused with the way out spelled in the message.
Any other name is deliberate nesting, and cmd/ls are untouched: they are
how a program in a pane is supposed to reach its own session.
</content>
</entry>
<entry>
<title>slosh: paint the padding ring, so a float cannot be seen through</title>
<updated>2026-08-22T16:30:18Z</updated>
<author>
<name>user</name>
<email>user@clank</email>
</author>
<published>2026-08-22T16:30:18Z</published>
<link rel='alternate' type='text/html' href='https://git.sl0p.foo/slosh.git/commit/?id=f5ce221ee853d430113063cb85c43bbea078818e'/>
<id>urn:sha1:f5ce221ee853d430113063cb85c43bbea078818e</id>
<content type='text'>
A tiled pane sits on a cleared screen, so unpainted padding looked fine by
accident; a float sits on whatever was composited under it, and every
unpainted cell let that show through. draw_frame now fills the ring between
border and contents with exactly what screen_clear leaves, and a suspended
pane's never-composed content rect gets the same treatment.
</content>
</entry>
</feed>
