<feed xmlns='http://www.w3.org/2005/Atom'>
<title>slosh.git, branch v0.1.1</title>
<subtitle>an opinionated terminal multiplexer in C with programmable pane chrome</subtitle>
<id>https://git.sl0p.foo/slosh.git/atom/?h=v0.1.1</id>
<link rel='self' href='https://git.sl0p.foo/slosh.git/atom/?h=v0.1.1'/>
<link rel='alternate' type='text/html' href='https://git.sl0p.foo/slosh.git/'/>
<updated>2026-08-27T14:31:56Z</updated>
<entry>
<title>pty: open the slave before the fork, so a pane's size reaches it</title>
<updated>2026-08-27T14:31:56Z</updated>
<author>
<name>Peter Geissler</name>
<email>peter@haxx.in</email>
</author>
<published>2026-08-27T14:31:02Z</published>
<link rel='alternate' type='text/html' href='https://git.sl0p.foo/slosh.git/commit/?id=b380d3541d71ee2a94b4bda3b99182eddd153851'/>
<id>urn:sha1:b380d3541d71ee2a94b4bda3b99182eddd153851</id>
<content type='text'>
On Darwin a pty master has no line discipline until its slave has been
opened: every TIOCSWINSZ on the master until then fails with ENOTTY and
the size is dropped without a word. A pane is born 1x1 and told how big
it really is by the layout an instant after the fork -- and with the
child doing the open(), that message raced the child and usually lost.
Nothing ever sent it again, so the program in the pane spent its whole
life believing it had one row and one column. `C-a e` hit it every
time, because the editor's pane is always brand new: nvim drew its
screen into two rows in the corner of a 94x15 pane.

The old code knew half of this -- the child set the winsize on the
slave it had just opened, which is what kept a fresh pane from being
0x0 -- but that only fixed the *spawn* size. Every resize after it
still went to a master that might not have a slave yet.

So open the slave here, before the fork, and let the child inherit it.
The fork copies the fd table, so the slave is open from the child's
first instruction and every resize the parent sends afterwards lands;
BSD's forkpty() does the same thing for the same reason. The winsize is
set on that slave before the fork, which orders it ahead of anything
the parent sends later, and the child no longer sets it at all: by the
time the child ran, the size it was spawned with could already be
stale, and writing it back would undo the very resize this is about.

test_pty_size.py asks the pane's own program what size it thinks it is
-- for the first pane, for a pane from a split, and after a resize --
and compares it to the pane. It printed "1 1" against a 74x18 pane
before this.

live_m0.py asked `tput cols`, which needs a terminfo entry for
xterm-ghostty and therefore fails on most macs for a reason that has
nothing to do with resizing. `stty size` asks the tty itself, which is
the thing under test.
</content>
</entry>
<entry>
<title>vendor build: pass ghostty its own version, not our tags</title>
<updated>2026-08-27T14:27:13Z</updated>
<author>
<name>user</name>
<email>user@clank</email>
</author>
<published>2026-08-27T14:27:13Z</published>
<link rel='alternate' type='text/html' href='https://git.sl0p.foo/slosh.git/commit/?id=21a2a39a3b27ff4625bbc1d89220fa83e923ecf8'/>
<id>urn:sha1:21a2a39a3b27ff4625bbc1d89220fa83e923ecf8</id>
<content type='text'>
The vendored tree has no .git, so upstream's version detection runs
git describe in *our* repository -- and the moment this repo grew its
first release tag (v0.1.0), ghostty's build refused it: 'tagged
releases must be in vX.Y.Z format matching build.zig'. Our v0.1.0 is
not ghostty's 1.3.2-dev, and upstream is right not to let a stranger's
tag name its build.

-Dversion-string is upstream's own escape hatch for exactly this. The
value is read from the vendored build.zig.zon rather than written here,
so a re-vendor updates it in the one place it already lives. Both
invocations (the host vendor target and the cross-built release legs)
get it through one VT_FLAGS.
</content>
</entry>
<entry>
<title>install docs: one subsection per OS</title>
<updated>2026-08-27T14:19:25Z</updated>
<author>
<name>user</name>
<email>user@clank</email>
</author>
<published>2026-08-27T14:19:25Z</published>
<link rel='alternate' type='text/html' href='https://git.sl0p.foo/slosh.git/commit/?id=b6fb41cea7610e56280a4db6572bbad955a9b667'/>
<id>urn:sha1:b6fb41cea7610e56280a4db6572bbad955a9b667</id>
<content type='text'>
'With Homebrew (macOS or Linux)' followed by a source build made every
reader parse which half was theirs; now macOS and Linux each have a
heading, and you read yours. macOS is the tap; Linux is the same tap
under Homebrew on Linux, or the source build, which the docs landing
page had never mentioned brew at all.

Also fixes the README telling people to run bare 'make', which prints
the help text (the default goal) and builds nothing. install.sh had
this figured out and commented on it; the README had not.
</content>
</entry>
<entry>
<title>README/docs: stop leaning on the aside dash</title>
<updated>2026-08-27T14:11:26Z</updated>
<author>
<name>user</name>
<email>user@clank</email>
</author>
<published>2026-08-27T14:11:26Z</published>
<link rel='alternate' type='text/html' href='https://git.sl0p.foo/slosh.git/commit/?id=238b1a7eb51c576254537705597f8a039e1d9c86'/>
<id>urn:sha1:238b1a7eb51c576254537705597f8a039e1d9c86</id>
<content type='text'>
Some 230 sentences hung their qualifications between dashes, and at that
density the tic reads as the writing's signature rather than a tool it
occasionally picks up. Each one is now the punctuation the sentence
actually wanted: a colon where the aside defines, parentheses where it
whispers, a comma where it barely pauses, a full stop where two thoughts
were pretending to be one. Definition lists and reference tables take
colons throughout.

Two double hyphens remain on purpose, both literal: the argv separator
in a shell example, and a table cell that means 'takes nothing'.
</content>
</entry>
<entry>
<title>README/docs: spell the dash --, as the rest of the tree does</title>
<updated>2026-08-27T14:04:37Z</updated>
<author>
<name>user</name>
<email>user@clank</email>
</author>
<published>2026-08-27T14:04:37Z</published>
<link rel='alternate' type='text/html' href='https://git.sl0p.foo/slosh.git/commit/?id=5151e42f22b404cf41dc8b409bcdfcd4b39444ea'/>
<id>urn:sha1:5151e42f22b404cf41dc8b409bcdfcd4b39444ea</id>
<content type='text'>
Every C comment, commit message and generated config in this repo
writes the aside dash as a spaced double hyphen; the Markdown alone
used U+2014, some 220 of them. One spelling, and the ASCII one -- the
prose now matches the code it documents.

Mechanical (every occurrence was the spaced form, checked including
the one inside a code fence, where it was comment text), so the words
themselves are untouched.
</content>
</entry>
<entry>
<title>docs: the landing page stops arguing</title>
<updated>2026-08-27T14:04:37Z</updated>
<author>
<name>user</name>
<email>user@clank</email>
</author>
<published>2026-08-27T14:04:37Z</published>
<link rel='alternate' type='text/html' href='https://git.sl0p.foo/slosh.git/commit/?id=c76159dd380bf5a37d1a24e5295c42c626090c5c'/>
<id>urn:sha1:c76159dd380bf5a37d1a24e5295c42c626090c5c</id>
<content type='text'>
The 'Why another one' section was two paragraphs of case-making against
other multiplexers, and a landing page that defends its existence reads
as unsure of it. What the section said truthfully -- the vendored core,
recompute-not-remember, scriptable to its depth -- design.md already
says where it belongs. The one-line closer stays: 15k lines of C and no
dependencies is a fact, not a defence.
</content>
</entry>
<entry>
<title>docs: drop the ascii mockup from the landing page too</title>
<updated>2026-08-27T14:02:11Z</updated>
<author>
<name>user</name>
<email>user@clank</email>
</author>
<published>2026-08-27T14:02:11Z</published>
<link rel='alternate' type='text/html' href='https://git.sl0p.foo/slosh.git/commit/?id=d7742f16b81ef09e74c0cbe6999d07100e5ef63c'/>
<id>urn:sha1:d7742f16b81ef09e74c0cbe6999d07100e5ef63c</id>
<content type='text'>
Same reasoning as the README's (24a6db01): the sketch says nothing the
first paragraph did not, and on the rendered site it sat where the
reader was deciding whether to keep reading.
</content>
</entry>
<entry>
<title>README: drop the ascii mockup</title>
<updated>2026-08-27T13:58:35Z</updated>
<author>
<name>user</name>
<email>user@clank</email>
</author>
<published>2026-08-27T13:58:35Z</published>
<link rel='alternate' type='text/html' href='https://git.sl0p.foo/slosh.git/commit/?id=24a6db01b24b8ef4cfae42e18284e1168cbca0df'/>
<id>urn:sha1:24a6db01b24b8ef4cfae42e18284e1168cbca0df</id>
<content type='text'>
Sixteen lines of box-drawing was the biggest thing on the page and the
least true: a static sketch of two frames says nothing the first
paragraph had not already said, and the real look -- themes, compact
borders, shaders -- is not something a code block can be honest about.
The docs site keeps its copy; the README gets to the install command
sooner.
</content>
</entry>
<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>
</feed>
