| Commit message (Collapse) | Author | Age |
| | |
|
| |
|
|
|
|
|
|
| |
The build/ pattern matched every directory of that name, not just our
build output at the root -- so vendor/libghostty-vt/src/build/ (the
library's own build.zig helpers) never made it into git, and make
vendor broke on fresh clones. Anchor the ignore to /build/ and restore
the 284 files it had silently dropped.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
`ruff` is not a system package on most machines, and `pip install --user ruff` is
refused outright on a distro python (PEP 668) -- so the last two commits shipped a
formatter nobody could run without knowing to build a venv by hand. `make tools`
builds it:
$ make tools
ruff 0.16.3 in .venv
survives make clean; rm -rf .venv to start over
clang-format stays a system package, because everywhere it is one. `make tools`
says so if it is missing rather than pretending it handled it.
**The venv is looked in before `$PATH`, and the order is the point.** `ruff.toml`
now pins the version with ruff's own `required-version`, so a ruff of the wrong one
refuses to run and names both versions. Preferring `$PATH` would then strand
somebody who has 0.14 from their package manager and 0.16.3 sitting in the `.venv`
the repo just built for them -- refused by the tool, with the fix already on disk
and not being looked at. Tested with exactly that: 0.14.0 on `$PATH`, `make
fmt-check` still clean.
The pin exists because a formatter whose output moves between releases turns
"formatted" into "formatted by whoever committed last", and the diff lands on the
next person to touch the file. Refusing loudly is better than reformatting quietly.
Bumping it is one line in `ruff.toml`, one in the Makefile, and a reformat commit,
which is the honest cost of a new version.
The hook resolves ruff the same way, off `git rev-parse --show-toplevel` so it works
from a subdirectory, and its "not installed" message now says `make tools` instead
of naming three package managers and hoping.
Verified from the state a fresh clone is in: with no ruff anywhere, `make fmt` says
`ruff not found. run: make tools` and stops; after `make tools` (2.6s) both `fmt`
and `fmt-check` work with nothing on `$PATH`; the hook formats a staged Python file
using the venv; the venv survives `make clean`; and `.venv/` is ignored.
`make fmt` over the whole tree after all this changed nothing, which is the useful
part: the pinned version agrees with what is already committed.
One near-miss worth recording: the `.gitignore` line for `.venv/` had to be written
twice. A `git reset --hard` undoing a throwaway probe commit took the working tree
with it, including that edit -- which is what `--hard` does, and why the state was
checked afterwards rather than assumed.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Adding an effect meant editing shader.c and rebuilding, which is fine for us
and useless to anyone else. Now any `*.so` in the shader directory that
exports a plugin table is loaded at startup, and what it declares is named in
the config exactly like a built-in -- same syntax, same three parameters,
same pass. Default location is a `shaders` directory beside the config file,
so dropping the .so next to config.kdl is the whole installation.
The interesting part is what a plugin can see, which is one header.
shader_abi.h holds cell_t, color_t, shade_ctx_t, shader_t and nothing else:
no libghostty-vt, no pty, no layout, no session. A shader is therefore
unable to reach anything but the cell it was handed, not by rule but because
nothing else is declared where it can find it. cell_t and color_t moved
there from sl0ppty.h for the same reason -- that is the one place they are a
contract with code compiled outside this tree rather than an internal type.
Loading is deliberately narrow:
- refused unless the library reports our ABI version *and* matching struct
sizes. The version says which contract; the sizes catch someone editing
their copy of the header, which a version number alone calls fine and a
running session calls garbled colours;
- refused if it redefines an existing name, because a config naming `dim`
means the `dim` it was written against;
- all-or-nothing per file, and per file: a bad library is a line on stderr
and everything else still loads (D9 fail-open);
- nothing is ever unloaded. A shade_fn may be sitting in a live config or on
a pane, so dlclose() is a jump into an unmapped page. A reload picks up
newly *added* plugins; replacing one needs a new session.
This is the single exception to D9's "a plugin is a subprocess", and it is
argued in D15 rather than drifted into: a shader is called per cell, per
pass, per frame, which no subprocess can be in the path of, and the measured
alternative -- interpreting bytecode per cell -- is 56ns against 8ns for the
compiled function. On trust, `shell` and a layout's `command=` already let a
file in the config directory run anything as you, so a .so beside them is
not a new boundary. The docs say so plainly rather than implying a sandbox.
contrib/shader-plugin is a working skeleton: Makefile, two commented effects
(one positional, one animated), and the three rules a shader has to keep. It
is built and loaded by the test suite, so a broken example is a failing test
rather than someone else's afternoon.
|
| | |
|
|
|
Design contract in DESIGN.md. libghostty-vt vendored fresh from ghostty
main@bedb6f2 (zig 0.16), built to a 14MB static lib.
M0: pty spawn (no libutil), a lib-vt terminal per pane, a screen cell buffer,
a diff emitter that repaints only what changed, raw mode, SIGWINCH, and a
~120Hz frame cap that coalesces pty reads.
Also the seed of the headless harness: --headless dumps the composited screen
without a tty, so composition is testable in milliseconds.
tests/live_m0.py covers what headless cannot: raw mode, the diff emitter,
resize propagation, and the quit path. 6/6 green.
|