Was brought up as possibly being a build issue here:
https://codeberg.org/vancluever/translate-c/pulls/1
I do think that this is the better approach and seems to be the close
equivalent of the `.zig_ilb` option that's coming with `LazyPath` in
0.17.0 (which is how translate-c behaves there).
I was looking for something like this initially and I _think_ I might
have passed over it to start with because it was a bit hard to determine
the circumstances that `b.graph.zig_lib_directory` would be null, but
upon further examination, I think such cases would be rare if they
happened at all. Rather than default to the cwd in this event though I
just get it to error out - that way we'll know if it ever is the case!
Move DECRQSS response encoding into the terminal DCS handler so both the
full termio path and libghostty-vt terminal stream emit the same
replies. The C API stream now maintains and releases DCS parser state
and forwards responses through write_pty.
Move DECRQSS response encoding into the terminal DCS handler so both
the full termio path and libghostty-vt terminal stream emit the same
replies. The C API stream now maintains and releases DCS parser state
and forwards responses through write_pty.
The custom memset accepted its fill value as u8 even though C callers
pass int. Accept c_int and explicitly truncate it to the low byte, which
is what other implementations of this do.
DECRQSS previously serialized every active underline as SGR 4, which
caused double, curly, dotted, and dashed styles to round-trip as single
underlines.
Emit the 4:n form for extended underline styles while retaining the
legacy 4 form for single underlines, and cover every supported style.
Since the renderer thread now emits scrollbar events on almost every
frame, there's always a `.scrollbar` message for the dying surface in
the app mailbox.
The OS runtime seems to schedule `appTick` and `ghostty_surface_free`
differently across macOS pre-26, 26 and 27.
On macOS 26.x, `ghostty_app_free` happens after
`App.scrollbar(_:target:v:)`, leaving `surface.userdata` pointing at a
freed `SurfaceView`.
When `deinit` runs on the main thread, free the surface synchronously
instead of detaching to a task. This fixes both crashes mentioned in
https://github.com/ghostty-org/ghostty/pull/9512 and
https://github.com/ghostty-org/ghostty/issues/13359.
### AI Disclosure
I used Claude to analyze the backtrace, but the code is written and
tested by myself.
DECRQSS previously serialized every active underline as SGR 4,
which caused double, curly, dotted, and dashed styles to round-trip
as single underlines.
Emit the 4:n form for extended underline styles while retaining the
legacy 4 form for single underlines, and cover every supported style.
The custom memset accepted its fill value as u8 even though C callers
pass int. Accept c_int and explicitly truncate it to the low byte, which
is what other implementations of this do.
This PR provides an implementation for the
[vicinae-hotkey-v1](https://github.com/vicinaehq/vicinae-wayland-protocols/tree/main/staging/vicinae-hotkey)
protocol, as discussed
[here](https://github.com/ghostty-org/ghostty/discussions/13453).
This is a wayland protocol that allows the client to dynamically
negotiate global shortcuts with the compositor. Unlike the portal, the
clients are free to bind, rebind, and unbind global shortcuts they
reserve.
Here are a few advantages of using this over the global shortcut portal
for ghostty specifically:
- `vicinae-hotkey-v1` lets the client know the state of its bindings at
all time, if a global bind is not granted by the compositor the cllient
is notified with a descriptive error message which is designed to help
the user understand what the problem might be. In my implementation, I
decided I would show a desktop notification to the user in case a global
shortcut reservation fails.
- Global binds set in the config cannot drift from what is actually
registered, cannot be unilaterally changed by the user in compositor
settings, and do not pollute the global shortcut namespace permanently.
Reservations are only active while ghostty is running.
- The protocol provides the client with an input serial that can be used
to generate an `xdg_activation` token, allowing ghostty to steal focus
when one of its global shortcut is used. Currently ghostty doesn't have
an input serial to pass to `xdg_activation`. I didn't wire it for now,
in order keep things simple. But I guess it will be a nice to have.
---
AI disclosure: most of the code was written by Fable 5 (Claude Code), as
zig is not my primary language.
From an implementation perspective: I made it so that
`vicinae-hotkey-v1` is used to manage global shortcuts over the portal
when the global is advertised by the compositor. If it's not available,
we fallback on the portal like before.
At this time the protocol is implemented by Hyprland (since
[v0.56.0](https://github.com/hyprwm/Hyprland/pull/15010)) and there is
an open PR for [niri](https://github.com/niri-wm/niri/pull/4145). There
is also an official [wayland-protocols
proposal](https://gitlab.freedesktop.org/wayland/wayland-protocols/-/merge_requests/525).
As for the clients, for now the only implementer (that I know of) is
[vicinae](https://github.com/vicinaehq/vicinae).
The idea would be to merge this `vicinae-hotkey-v1` implementation and
then have it be superseded by the upstream version later, assuming it is
turned into an official extension protocol following the
wayland-protocols process.
PS: sorry for the diff noise about all the .po changes, I'm not sure
whether this is intended or not, but I did regenerate the translations
as asked
Resizing tabstops to an already-supported width previously allocated and
copied an equally sized dynamic buffer because the capacity check
excluded equality. Treat an exactly sized buffer as sufficient, avoiding
the temporary allocation and copy.
Add a fixed-buffer regression test so an unnecessary second allocation
fails the test.
Environment variables from the "real" environment leaked into the test
after the Zig 0.16 update which would cause them to fail if you ran them
on a Gnome system.
Environment variables from the "real" environment leaked into the test
after the Zig 0.16 update which would cause them to fail if you ran them
on a Gnome system.
Replaces: https://github.com/ghostty-org/ghostty/pull/13427
Zig's Mach-O linker does not emit LC_ENCRYPTION_INFO_64 for physical iOS
dylibs. This allows libghostty-vt to build successfully but causes
frameworks containing it to fail App Store validation.
I think it'd be cleaner to always just build Apple targets on Apple
hosts with the native linker. We don't need to rely on Zig being correct
and this helps ensure compatibility for details like this.
Replaces: https://github.com/ghostty-org/ghostty/pull/13427
Zig's Mach-O linker does not emit LC_ENCRYPTION_INFO_64 for physical
iOS dylibs. This allows libghostty-vt to build successfully but causes
frameworks containing it to fail App Store validation.
I think it'd be cleaner to always just build Apple targets on Apple hosts
with the native linker. We don't need to rely on Zig being correct and
this helps ensure compatibility for details like this.
Xcode 27's math.h uses the __need_infinity_nan protocol provided by
matching Clang resource headers. Zig 0.16's bundled float.h predates
that protocol, causing the bundled libc++ compilation to fail.
Overlay the SDK math.h through the Apple SDK libc include path and
provide the missing infinity and NaN definitions. The compatibility
header can be removed once Zig's bundled Clang headers support the
protocol.
Xcode 27's math.h uses the __need_infinity_nan protocol provided by
matching Clang resource headers. Zig 0.16's bundled float.h predates
that protocol, causing the bundled libc++ compilation to fail.
Overlay the SDK math.h through the Apple SDK libc include path and
provide the missing infinity and NaN definitions. The compatibility
header can be removed once Zig's bundled Clang headers support the
protocol.
#13417
The bundled upstream libc++ headers in Zig 0.16 skip the
Apple-configured availability setting. This causes the headers to assume
every LLVM 21 ABI symbol is present in the target system libc++,
producing binaries that fail at launch on macOS versions without
`std::__hash_memory`.
Enable the Apple vendor availability table for compile steps configured
by the Apple SDK helper. libc++ now selects its inline compatibility
implementation when the target system dylib does not provide the symbol.
References in the mega comment
#13417
The bundled upstream libc++ headers in Zig 0.16 skip the Apple-configured
availability setting. This causes the headers to assume every LLVM 21
ABI symbol is present in the target system libc++, producing binaries
that fail at launch on macOS versions without `std::__hash_memory`.
Enable the Apple vendor availability table for compile steps configured
by the Apple SDK helper. libc++ now selects its inline compatibility
implementation when the target system dylib does not provide the symbol.
References in the mega comment
Closes#12228
Supersedes #12388
**UPDATED** - Also check comments for additional details!
This commit represents the majority of the work necessary to upgrade
Ghostty to use Zig 0.16.0.
At this point, all tests pass under Linux, but more work may be
necessary to get them to build and function on other platforms.
There are some parts of this update that deserve commentary, so that
follows below:
## Expanded use of global state (IO/environment related)
Global state, once generally only used by the C library, has now been
expanded to be used across the project at large. The static local
variable that holds the state has been moved private in its source
container with all attributes that need to be accessed globally gated
behind accessors, most of which guard on testing and send test copies
instead. Use of the global state in non-testing scenarios asserts that
the state has been initialized through `init` naturally through the
optional assertion process.
The rationale for this change is to have a location to store a
general-purpose I/O implementation and environment variables, both of
which are now provided through [Juicy
Main](https://ziglang.org/download/0.16.0/release-notes.html#Juicy-Main)
and hence can no longer be accessed or mutated through stdlib without
use of lower-level system calls and hacks (some of which are employed,
but sparingly).
As the code matures, dependence on global state should naturally slim
down.
We do not allow global state to be used in libghostty-vt. There are
comptime guards that prevent this should compilation of libghostty-vt
end up pulling `global.zig`. This means that as per the last paragraph,
work has already begun to de-couple the codebase from global state where
necessary. Additionally, in some places where environment needs to be
updated and where it can be done in an isolated fashion, environment
maps are used - system-level injection of environment through the use of
`setenv` or `unsetenv` now only happens during early initialization (and
hopefully we can remove these in the future too, especially since they
require re-synchronization of the higher-level environment primitives
after this is done).
## The `lib/compat` Tree
Some stdlib features that have been removed but still either seem they
would be valuable to us or outright complex to move away from
(particularly `SegmentedList`) have been extracted from 0.15.2, updated
as needed, and placed in `src/lib/compat`. The intention again is to
allow for piecemeal migration to more modern implementations or possibly
straight local versions.
This paradigm has also allowed us to add `std.Io.Condition.waitTimeout`,
which incidentally was missed in the 0.16.0 shuffle and has been
re-added for 0.17.0. We can remove this in favor of the upstream when we
eventually migrate to that, obviously.
Note that there was a lot more of this extracted code when this work was
started, but a lot of said code has been removed (namely environment or
process/fd-related functionality).
## translate-c Issues (functional on Linux, Darwin WIP)
There have been a number of C translation issues that we have been
working through through submitted patches and the great help from folks
on the Arocc and Zig side. This is ongoing, with the remaining work to
getting things fixed mainly focused on the MacOS side. Stay tuned for
further developments.
As mentioned at the top, follow comments for more details!
Restore the error handling that the removed std.posix fork and waitpid
wrappers previously provided. Raw fork failures now propagate, waitpid
retries interruptions before reading status, and edit-config constructs the
sentinel-terminated argv required by execve.
Let global initialization own cleanup through its existing errdefer so
temporary paths are freed once. Report initialization failures with the
static synchronous I/O provider because global I/O has already been torn
down by that point.
Zig 0.16.0 made the criteria for reporting "failed command" stricter (or
looser, depending on your perspective I guess...) - now, tests that
print anything to stderr cause the message to appear.
Note that in this instance tests still pass and you get a return code of
0, but nonetheless, it can be confusing.
Additionally, having spammy passing tests in general is not necessarily
a great experience, so this should help with that.
Note that this change was already done to the main tests. We can add a
build argument to control this if need be.
The early-stage main Zig wrapper recognizes if main only needs the
minimal state (args and lower-level environment) and skips a bunch of
unneeded initialization (allocator, arena, threaded I/O, and the
higher-level environment map). Particularly, the fact that it does not
set up an I/O instance means that we won't have any unneeded signal
handlers set up for the unused threaded I/O implementation, which is
similar in spirit to the fixes we applied for the C VT implementation,
with the notable difference that we do actually set a threaded I/O up in
global state - hence, again, we don't want the duplicate unused one.
The C API is assumed to be single-threaded per VT instance.
Additionally, using fully-threaded I/O instances registers signal
handlers, and would do a pair of registrations once per instance, which
could easily get out of hand (and is not really what we intend anyway).
init_single_threaded does not register signal handlers, so it does not
have this issue, and matches the execution model of the C VT API
(single-threaded/not thread-safe within a single VT instance).
This also fixes an initialization issue with the threaded I/O instance
in general (needs allocation as the memory location would have gone out
of scope before).
- benchmark: avoid buffers to avoid a memcpy
- build: keep frame pointers on macOS. There was some debug changes from
Zig 0.15 and this helps. Also, Apple actually requires/expects x29 to
always be a frame pointer.
- build/macos: force libSystem symbols instead of compiler-rt
- global: add InitOpts.tool so that ghostty-gen/bench can parse their
own actions in `+action`
- quirks: provide our own vectorized memset. see the comment for more
details why.
- synthetic: fix UB by accessing global.io before it was initialized
- terminal/hash_map: force inline for unique repr types. Zig 0.15
inlined and 0.16 doesn't, measured a huge slowdown in hyperlink
benchmarks.
- terminal: add explicit `@Vector` usage for storing a run of identical cells
as well as for scanning printable cells. This auto-vectorized in Zig
0.15 but not in Zig 0.16. This produces the same assembly.
- unicode: properties and LUT need power-of-two backing integer to avoid
bad LLVM codegen
This commit represents the majority of the work necessary to upgrade
Ghostty to use Zig 0.16.0.
Key parts:
* In addition to its previous responsibilities, the global state now
houses state for global I/O implementations and the process
environment. It is now also utilized in the main application along
with the C library. Where necessary, global state is isolated from key
parts of the implementation (e.g., in libghostty subsystems), and it's
expected that this list will grow.
* We currently manage our own C translation layer where necessary. In
these cases, cImport has been removed in favor of the new external
translate-c package. Due to fixes that have needed be made to properly
translate the dependencies that were swapped out, as mentioned, we
have had to backport fixes from the current translate-c package (and
the upstream Arocc dependency). We will host this ourselves until Zig
0.17.0 is released with these fixes.
* Where necessary (only a small number of cases), some stdlib code from
0.15.2 (and even from 0.17.0) has been taken, adopted, and vendored in
lib/compat.
Co-authored-by: Leah Amelia Chen <hi@pluie.me>