Remove BLAKE3 prefix digests. Keep READY/FINISH as empty records since
they're semantically important markers.
Our existing format (CRC32 per-record, declared counts, strict tag
ordering requirements, etc.) already detect: accidental corruption,
truncation, data omission, and duplication.
BLAKE3 only protects against valid records being swapped or removed
entirely. It is heavy for just that, and callers can solve that anyways
via their own transport (like, just use TCP). For more adversarial
protection, callers can also add layers like TLS or their own alternate
signing methods depending on their own threat models.
Removing the hash improves encode times by ~1.4x, decode times by ~1.3x.
Time-to-READY decoding is effectively unchanged because it was such a
small package to begin with.
**AI usage:** I had it clean up the comments and the tests, but I did
the blake3 removal and marker changes, and wrote the commit message
myself. All reviewed.
This fixes regressions in the flatpak/snap builds, and knock-on stuff
that was discovered as as a result:
* Update the Zig versions in the flatpak/snap build configuration files.
* Restore the classic `-Dpatch-rpath` option, and add a new
`-Dpatch-interp` option. This ensures that the snap can still use
`-Dpatch-rpath` correctly.
* There seems to be an issue in Zig when parsing IPv6 addresses that
leads to issues loading `resolv.conf` files; when trying to load a
nameserver that has an IPv6 address with a numeric interface index as
the scoped zone ID, Zig will try to resolve the interface as a name
rather than just use the index. This is coming up in snap builds because
the build process seems to, by default, use the exhaustive
`/run/systemd/resolve/resolv.conf` file, versus the simpler stub
(`stub-resolv.conf`) file. We work around this for the time being by
linking the stub at the end of the Zig part, overwriting the link to the
non-stub file.
* Fixed `gtk4-layer-shell` packaging - the migration to external
translate-c meant that non-system builds of the dependency were not
handing the local `gtk4-layer-shell` headers over for translation. Now,
instead, we've extracted the management of the `gtk4-layer-shell` source
and `wayland-protocols` generation to a locally-cached object so that
the source can be shared by both C translation and the library build in
a way that is not coupled to any particular step.
Catch NULL results from CoreFoundation/CoreText creation functions and
return error.OOM rather than null derefs later. I verified that this is
possible but didn't verify the behavior when it happens, this is just
defensive based on the report here: #13671 because it costs us nothing
really.
Discard the selection notification payload before debouncing
accessibility changes.
The debouncer previously retained the notification and its surface
object, keeping a closed tab's view and PTY alive after the undo
timeout.
I was messing around with this tool the other day on a 60% keyboard so I
thought this would be a nice addition for situations like that. Keeps in
line with the vi/less j and k inputs that this tool has as well.
Remove BLAKE3 prefix digests. Keep READY/FINISH as empty records since
they're semantically important markers.
Our existing format (CRC32 per-record, declared counts, strict tag ordering
requirements, etc.) already detect: accidental corruption, truncation,
data omission, and duplication.
BLAKE3 only protects against valid records being swapped or removed entirely.
It is heavy for just that, and callers can solve that anyways via their
own transport (like, just use TCP). For more adversarial protection,
callers can also add layers like TLS or their own alternate signing
methods depending on their own threat models.
Removing the hash improves encode times by ~1.4x, decode times by ~1.3x.
Time-to-READY decoding is effectively unchanged because it was such a
small package to begin with.
Catch NULL results from CoreFoundation/CoreText creation functions and
return error.OOM rather than null derefs later. I verified that this is
possible but didn't verify the behavior when it happens, this is just
defensive based on the report here: #13671 because it costs us nothing
really.
This fixes regressions in the flatpak/snap builds, and knock-on stuff
that was discovered as as a result:
* Update the Zig versions in the flatpak/snap build configuration files.
* Restore the classic -Dpatch-rpath option, and add a new -Dpatch-interp
option. This ensures that the snap can still use -Dpatch-rpath
correctly.
* There seems to be an issue in Zig when parsing IPv6 addresses that
leads to issues loading resolv.conf files; when trying to load a
nameserver that has an IPv6 address with a numeric interface index as
the scoped zone ID, Zig will try to resolve the interface as a name
rather than just use the index. This is coming up in snap builds
because the build process seems to, by default, use the exhaustive
/run/systemd/resolve/resolv.conf file, versus the simpler stub
(stub-resolv.conf) file. We work around this for the time being by
linking the stub at the end of the Zig part, overwriting the link to
the non-stub file.
* Fixed gtk4-layer-shell packaging - the migration to external
translate-c meant that non-system builds of the dependency were not
handing the local gtk4-layer-shell headers over for translation. Now,
instead, we've extracted the management of the gtk4-layer-shell source
and wayland-protocols generation to a locally-cached object so that
the source can be shared by both C translation and the library build
in a way that is not coupled to any particular step.
Discard the selection notification payload before debouncing
accessibility changes.
The debouncer previously retained the notification and its surface
object, keeping a closed tab's view and PTY alive after the undo
timeout.
There're won't be any visible errors, but
`keybind=cmd+r=toggle_tab_overview/toggle_window_decorations/size_limit/quit_time`
shouldn't go to `showChildExited`
## AI Disclosure
Found by Claude during another quest, but I changed on myself.
ABI BREAKING: This removes `ghostty_terminal_mode_get` and `_mode_set`.
We can now represent these operations completely with standard
`ghostty_terminal_get` and `ghostty_terminal_set`, which makes it much
more flexible to preserve ABI in the future.
This is all centered around a new `GhosttyTerminalModeConfig` structure
that is an in or out parameter depending on use case.
This also adds a new `GHOSTTY_TERMINAL_OPT_MODE_DEFAULT` option that can
be used to set the _default_ value of mode that happens when a RIS event
(full reset) is sent. Note that not all modes are configurable because
some are set based on live terminal state and aren't modes in and of
themselves.
## Why Delete Functions? Why Not Add?
Once tagged, the goal of `libghostty-vt` is to remain HIGHLY ABI
compatible. We are striving for top tier ABI compatibility similar to
legendary C libraries. That means we need to be highly confident in our
API shapes: functions, structs, etc. and using shapes that we can retain
ABI compatibility even as we add features. Every function is a risk. By
pushing stuff into our `_get/_set` patterns, its easier to maintain ABI
compatibility.
Related to #10651
Default Ghostty dependency builds to libghostty-vt-only mode and avoid
initializing anything that would trigger broader dependency
requirements.
The impact of this is that Zig consumers can import ghostty-vt without
requiring Xcode on macOS.
ABI BREAKING: This removes `ghostty_terminal_mode_get` and `_mode_set`.
We can now represent these operations completely with standard
`ghostty_terminal_get` and `ghostty_terminal_set`, which makes it much
more flexible to preserve ABI in the future.
This is all centered around a new `GhosttyTerminalModeConfig` structure
that is an in or out parameter depending on use case.
This also adds a new `GHOSTTY_TERMINAL_OPT_MODE_DEFAULT` option that
can be used to set the _default_ value of mode that happens when a RIS
event (full reset) is sent.
Related to #10651
Default Ghostty dependency builds to libghostty-vt-only mode and
avoid initializing anything that would trigger broader dependency
requirements.
The impact of this is that Zig consumers can import ghostty-vt without
requiring Xcode on macOS.
The purpose of SegmentedPool was pointer-stable values for the pty write
path, and the std.MemoryPool provides that.
SegmentedPool is actually so old it predates a stdlib memory pool! Just
noting why I did it in the first place. I also wrote it when I was
pretty fucking bad at Zig, so I'm shocked its lasted this long.
The write path is hot , so the replacement was benchmarked against the
old SegmentedPool plus a rewrite simple Pool I did before realizing...
wait... why not just a MemoryPool. Benchmarked using the real 240-byte
xev write request.
```
workload old std.MemoryPool
depth-1 (keystroke echo) 3.93 ns/op 0.96 ns/op
burst (1MiB paste, d=256) 4.27 ns/op 1.00 ns/op
cold growth (32 -> 16k) 4.54 ns/op 6.48 ns/op
malloc create/destroy 15.9 ns/op (baseline)
```
Cold growth is slower but this is only a cost when the pool grows.
Note this also gets rid of the preallocation, which didn't show any
measurable performance benefit at all. This has the benefit of shrinking
our ThreadData by ~10KB.
This was motivated by #13655
The purpose of SegmentedPool was pointer-stable values for the pty write
path, and the std.MemoryPool provides that.
SegmentedPool is actually so old it predates a stdlib memory pool!
Just noting why I did it in the first place. I also wrote it when I was
pretty fucking bad at Zig, so I'm shocked its lasted this long.
The write path is hot , so the replacement was benchmarked against the old
SegmentedPool plus a rewrite simple Pool I did before realizing...
wait... why not just a MemoryPool. Benchmarked using the real 240-byte xev
write request.
workload old std.MemoryPool
depth-1 (keystroke echo) 3.93 ns/op 0.96 ns/op
burst (1MiB paste, d=256) 4.27 ns/op 1.00 ns/op
cold growth (32 -> 16k) 4.54 ns/op 6.48 ns/op
malloc create/destroy 15.9 ns/op (baseline)
Cold growth is slower but this is only a cost when the pool grows.
Note this also gets rid of the preallocation, which didn't show any
measurable performance benefit at all. This has the benefit of shrinking
our ThreadData by ~10KB.
This fixes#13647 by using at most one GStreamer thread per application.
This was previously addressed in #12815 which used at most one GStreamer
thread per surface. Originally discussed in #12808.
This fixes#13647 by using at most one GStreamer thread per application.
This was previously addressed in #12815 which used at most one GStreamer
thread per surface. Originally discussed in #12808.
Image eviction removed associated placements from storage without
deinitializing them. Pin-backed placements therefore remained registered
with the screen after eviction, allowing graphics-heavy output to
accumulate stale tracked pins.
Pass the owning screen through image insertion and eviction, and
deinitialize each placement before removing it. Cover both the released
pin and a retained image's live pin in the eviction regression test.
Surface.handleMessage allocated a null-terminated copy for every working
directory update. OSC 7 values fit within the parser's 2 KiB fixed
buffer, so use stack-fallback storage sized for that bound and its
terminator.
The message type does not enforce the OSC bound, so an oversized future
producer still falls back to the heap. performAction already borrows the
value only for the duration of the call, preserving its existing
lifetime.
Image eviction removed associated placements from storage without
deinitializing them. Pin-backed placements therefore remained registered
with the screen after eviction, allowing graphics-heavy output to
accumulate stale tracked pins.
Pass the owning screen through image insertion and eviction, and
deinitialize each placement before removing it. Cover both the released
pin and a retained image's live pin in the eviction regression test.
Co-authored-by: Tim Culverhouse <tfc@ampcode.com>
Surface.handleMessage allocated a null-terminated copy for every working
directory update.
Use a small 256-byte stack-fallback buffer for common working directory
lengths without adding significant pressure to this deep call stack. Longer
paths retain the existing heap behavior, and performAction continues to borrow
the value only for the duration of the call.
Matches are sorted in the following order:
leadingColor > title > subtitle > description.
Ranking is lexicographic on (colorScore, textScore)
<img height="300" alt="image"
src="https://github.com/user-attachments/assets/1ec99e67-537e-4fc6-b595-d7eec8cbf31d"
/>
### AI Disclosure
Claude reviewed and added unit tests, also did some refactoring of my
original implementation.
Track each image's placement count in its existing metadata. This lets
us use constant-time usage checks (rather than scans) during eviction.
Select the best candidate directly from storage on each eviction,
preserving the existing priority order: unused status, transient hint,
generation, then ID.
Since eviction no longer allocates, it can't fail, so callers no longer
need to handle out-of-memory conditions.
Fixes#13074
Overlapping clipboard confirmations now defer denial until the next main
queue turn rather than completing inside the confirmation callback.
This prevents the native request state from being invalidated while its
callback is still active, avoiding the OSC 52 crash reported in #13074.
The deferred closure retains the originating surface view and completes
the ignored request with empty data, preserving the existing deny
behavior.
Fixes#13276
Make CachedValue safe for concurrent terminal content reads and expiry.
The expiry task could previously release cached Swift String storage
while another thread retained it, aborting the process during otherwise
normal terminal use.
Protect cached values and task handles with an NSLock, and exercise
concurrent reads across repeated expiration in a regression test.
Fix d=p and d=c point deletion so only placements intersecting the
target cell are removed.
Previously, placements spanning multiple rows could be deleted from
columns outside the target because the page-order comparison flattened
row and column coordinates.
Check the rectangle's column independently and use page order only for
its row span, matching Kitty's implementation:
https://github.com/kovidgoyal/kitty/blob/master/kitty/graphics.c
NOTE: I did not look at Kitty's source prior to fixing this. I only
referenced it after the fix to verify that the behavior matches.
Spec:
https://sw.kovidgoyal.net/kitty/graphics-protocol/#deleting-images
Fixes#13266
Keep search text and its selection range synchronized as a single state
transition.
Deleting or replacing a search term could leave a String.Index range
from the old value attached to the text field. Applying that range could
crash the app.
Clear selection before publishing new text.
Debug libghostty-vt dependencies embedded in ReleaseFast or ReleaseSmall
binaries no longer panic when narrow text overwrites the tail of a wide
glyph.
Replace the root module's std.debug.runtime_safety gate with
build_options.slow_runtime_safety so mixed optimization modes use the
dependency's safety configuration consistently.