GtkGLArea had numerous downsides that forced us to invent unsightly
hacks in our renderer to work around them, most chiefly the fact that it
holds its own GdkGLContext on the main thread (GL contexts are not at
all thread-safe), forcing us to keep our GL calls on the main thread. It
also does not interact well with triple-buffering and initialization is
forced to be this sort of deferred song-and-dance since we need to wait
for the GLArea to initialize its GL context before we can initialize the
renderer, the core surface, and then most things in the GTK surface.
We instead invent our own custom widget named RenderSurface that takes
simple DMABUFs and displays them. The task of obtaining a GL context
falls to manual EGL bindings, since we also need EGL to export OpenGL
textures into DMABUFs. We keep the EGL context solely on the render
thread meaning that the main thread never concerns itself with rendering
except when being notified that the renderer has pushed a new frame.
What makes this extra significant is that now the entire GTK apprt no
longer depends on OpenGL in any way, shape or form. As long as it is
being fed DMABUFs, it can render from whichever graphics API you want.
This means we can add more backends based on OpenGL ES or more likely
Vulkan rather painlessly in the future.
**AI disclosure**: I came up with the idea and let Pi implement most of
the nitty-gritty details around EGL, as well as replumbing the renderer
and cleaning up all the GTK-specific workarounds there. I then carefully
vetted every line of code and spent roughly as much time reviewing as
coding. Most of the documentation and all commit messages are in my own
words.
This commit refactors our use of translate-c, in preparation for larger
removal of cImport and better co-ordination between building of C
dependencies and translation of headers.
The major update is the creation of an internal helper package that
wraps our use of the external translate-c library. This allows us to not
only have better shorthand and a data-driven, declarative approach to C
translation (versus the otherwise more imperative approach), it also
funnels the external dependency into a single package instead of
spreading it out among what will be an increasingly larger amount of
places as dependencies in "pkg/" get updated.
It also includes some refactors, namely to the harfbuzz package, which
has had its individual settings refactored into helpers to allow for the
settings to be better shared between translation and the build of the
c-based static library.
Wuffs has also had a bit of a refactor too so that we don't generate a
file with all of the macro defines in it - we just send these in as "-D"
flags now.
GtkGLArea had numerous downsides that forced us to invent unsightly hacks
in our renderer to work around them, most chiefly the fact that it holds
its own GdkGLContext on the main thread (GL contexts are not at all
thread-safe), forcing us to keep our GL calls on the main thread.
It also does not interact well with triple-buffering and initialization
is forced to be this sort of deferred song-and-dance since we need to
wait for the GLArea to initialize its GL context before we can initialize
the renderer, the core surface, and then most things in the GTK surface.
We instead invent our own custom widget named RenderSurface that takes
simple DMABUFs and displays them. The task of obtaining a GL context
falls to manual EGL bindings, since we also need EGL to export OpenGL
textures into DMABUFs. We keep the EGL context solely on the render
thread meaning that the main thread never concerns itself with rendering
except when being notified that the renderer has pushed a new frame.
What makes this extra significant is that now the entire GTK apprt no
longer depends on OpenGL in any way, shape or form. As long as it is
being fed DMABUFs, it can render from whichever graphics API you want.
This means we can add more backends based on OpenGL ES or more likely
Vulkan rather painlessly in the future.
**AI disclosure**: I came up with the idea and let Pi implement most of
the nitty-gritty details around EGL, as well as replumbing the renderer
and cleaning up all the GTK-specific workarounds there. I then carefully
vetted every line of code and spent roughly as much time reviewing as
coding. Most of the documentation and all commit messages are in my
own words.
This updates our own bundled fontconfig (for static builds) to 2.18.3.
Note that fontconfig has changed their build process a bit since this
has been updated last; they are leaning on the Autoconf (and Meson as
they are now deprecating use of Autoconf) toolchain(s) to now generate a
number of headers that are a part of the build process.
Since servicing this dependency in an effort to keep the build pure Zig
is starting to get more complex, I've added some documentation on how to
actually get a snapshot of the fontconfig repository in a state where
files can be looked for and copied over as needed. Otherwise, we might
want to in the future consider removing this altogether and just rely on
system integrations.
Co-Authored-By: Jeffrey C. Ollie <jeff@ocjtech.us>
This modifies our wuffs dependency so that it no longer requires libc.
This unblocks using wuffs from libghostty-vt on freestanding targets,
which we'll eventually want for some Kitty graphics stuff.
This modifies our wuffs dependency so that it no longer requires libc.
This unblocks using wuffs from libghostty-vt on freestanding targets,
which we'll eventually want for some Kitty graphics stuff.
The gaToRgba swizzle requested a YA_PREMUL source pixel format from
the wuffs pixel swizzler, but wuffs does not support YA_PREMUL as a
swizzle source. As a result, gaToRgba returned error.WuffsError for every input.
The path can't happen in Ghostty GUI today since our PNG decoding always
produces RGBA, but it is possible via libghostty that submit grey+alpha
directly.
This prevents us from pulling in test images that trigger some anti-virus
scanners. It's also smaller since it only has the necessary bits that we need.
This also updates to the latest release: 0.4.0-alpha.10.
The Apple Color Emoji fallback font was discovered with the generic
discovery path, which builds a CTFontCollection and runs system-wide
font matching. Since we know the exact font we want, we can look it
up directly with CTFontCreateWithName instead.
Use the standard ~/Android/Sdk capitalization for the Linux SDK fallback.
This lets NDK discovery work when neither ANDROID_NDK_HOME nor an SDK
environment variable is set.
Signed-off-by: Fredrik Fornwall <fredrik@fornwall.net>
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.
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.
Fixes#13293
Treat Core Video display link creation as optional when macOS has no
active displays. The previous error path reported every creation
failure as out of memory and aborted renderer initialization.
This also resyncs the display link on any display change so when
a display becomes available it re-adds itself.
Tabs created while the session is locked now initialize normally and
fall back to event-driven rendering without vsync.
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.
#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
- 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>
Currently, cross to Darwin uses the Darwin headers bundled with Zig.
However, if you're running a build _on_ Darwin, an error is thrown if
the SDK can't be found, even though the bundled headers are still
available.
Now, we continue to search for and prefer the installed SDK, but if it
can't be found, we fall back to the bundled headers rather than failing
the build.
This allows libghostty-vt to be cross-compiled for macOS from non-macOS
platforms. I've updated pkg/apple-sdk to fallback to Zig's embedded
macOS headers if the macOS SDK is not found.
Additionally, CombineArchivesStep has been updated to use Linux
tooling on Linux.
The previous runtime_detect.zig called std.zig.system.resolveTargetQuery
which pulled in the entire Zig target/CPU model table infrastructure for
every architecture (~4,000 symbols, ~175 KB of data tables, ~130 KB of
code). This bloated the binary by ~500 KB and shifted code layout enough
to cause a measurable icache/branch-predictor regression in unrelated
hot paths like the terminal parser (~20% more cycles for identical
instruction counts).
Replace with minimal, direct CPU feature detection per architecture:
CPUID + XGETBV inline assembly on x86, sysctlbyname on Darwin AArch64,
and getauxval/prctl via std.os.linux (direct syscalls, no libc) on
Linux for AArch64, PPC, S390x, RISC-V, and LoongArch.
Split into per-architecture files under src/detect/ for
maintainability.
This uses a custom fork of `hwy/targtes.cpp` that uses an extern
function written in Zig to use Zig's standard CPU detection to avoid
a dependency on Apple SDK headers.
This is on the path to removing Apple SDK requirements to build
libghostty-vt, but will require a lot more work outside of this. The goal
is to get this out of our external dependencies first and then we can
work on removing the internal side.
## Summary
> [!IMPORTANT]
> Stacked on #12214. Review that first. (i am targeting `main` so here
you will see the full changeset, including 12214
Two changes that make the static libghostty archive consumable by
external linkers (MSVC link.exe, .NET NativeAOT, Rust, Go, etc.):
**Fat static archive on all platforms**
The static archive previously only bundled vendored deps on macOS (via
libtool). On Windows and Linux the archive contained only the
Zig-compiled code, requiring consumers to find and link freetype,
harfbuzz, glslang, spirv-cross, simdutf, oniguruma, etc. separately.
Now all platforms produce a single fat archive:
- macOS: libtool (unchanged)
- Windows: zig ar qcL --format=coff (MSVC's lib.exe can't read
Zig-produced GNU-format archives, so we use the bundled LLVM archiver)
- Linux: ar -M with MRI scripts (same approach as libghostty-vt)
**MSVC ubsan suppression for C deps**
Zig's ubsan runtime can't be bundled on Windows (LNK4229), leaving
__ubsan_handle_* symbols unresolved. freetype, glslang, spirv-cross, and
highway already suppress ubsan. This adds MSVC-conditional suppression
to seven more: harfbuzz, libpng, dcimgui, wuffs, oniguruma, zlib, and
stb.
Gated on abi == .msvc so ubsan coverage is preserved on Linux/macOS.
## Test plan
- [x] zig build produces a fat ghostty-static.lib (~230MB) with ~200
object files
- [x] MSVC's lib /LIST can read the archive
- [x] .NET NativeAOT consumer resolves all symbols (0 unresolved)
- [x] Linux/macOS builds unaffected (ubsan remains enabled)
On macOS 26.4, AFL builds were picking up Nix compiler-wrapper
variables and Apple SDK target settings from the shell environment.
That caused afl-cc to drive the wrong linker and target configuration,
which broke even simple fuzz harness builds. Unset the Nix compiler and
linker environment in the fuzz dev shell so AFL++ uses the system or
Homebrew Apple toolchain directly.
Also force afl-cc to link with lld because the newer Apple linker
asserts on the custom sections emitted by AFL's LLVM
instrumentation. Finally, pin fuzz-libghostty to the host target so the
build does not inherit stray SDK targets from the environment.
Zig's ubsan runtime cannot be bundled on Windows (LNK4229),
leaving __ubsan_handle_* symbols unresolved when the static
archive is consumed by an external linker like MSVC link.exe.
freetype, glslang, spirv-cross, and highway already suppress
ubsan unconditionally. Add MSVC-conditional suppression to the
seven C dependencies that were missing it: harfbuzz, libpng,
dcimgui, wuffs, oniguruma, zlib, and stb.
The fix is gated on abi == .msvc so ubsan coverage is preserved
on Linux and macOS where bundle_ubsan_rt works.
This updates simdutf to my fork which has a SIMDUTF_NO_LIBCXX option
that removes all libc++ and libc++ ABI dependencies.
From there, the hand-written simd code we have has been updated to also
no longer use any libc++ features. Part of this required removing utfcpp
since it depended on libc++ (`<iterator>`).
libghostty-vt now only depends on libc.
utfcpp is a header-only dependency, so its package wrapper does not
need to link the C++ standard library. Keep the empty static archive
for build integration, but stop adding an unnecessary libc++
dependency.
The vendored Highway package was being built with libc++ even though
Ghostty only uses its runtime target selection and dispatch support.
That pulled in extra C++ runtime baggage from upstream support files
such as abort, timer, print, and benchmark helpers.
Build Highway in HWY_NO_LIBCXX mode, only compile the target dispatch
sources we actually need, and compile Ghostty's SIMD translation units
with the same define so the header ABI stays consistent. Replace the
upstream abort implementation with a small local bridge that provides
Highway's Warn/Abort hooks and the target-query shim without depending
on libc++.
This keeps the Highway archive down to the dispatch pieces Ghostty
uses while preserving the existing dynamic dispatch behavior. The
bridge is documented so it is clear why Ghostty carries this small
local replacement.
MSVC translates C enums as signed int, while GCC/Clang uses unsigned
int. The freetype Zig bindings hardcode c_uint for enum backing types,
causing type mismatches when compiling with MSVC target.
Fix by adding @intCast at call sites where enum values are passed to
C functions, and @bitCast for the glyph format tag extraction where
bit-shift operations require unsigned integers.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Zig unconditionally passes -nostdinc++ and adds its bundled
libc++/libc++abi include paths, which conflict with MSVC's own C++
runtime headers. The MSVC SDK directories (added via linkLibC)
already contain both C and C++ headers, so linkLibCpp is not needed.
This is the same fix already applied upstream to highway, simdutf,
utfcpp, glslang, SharedDeps, and GhosttyZig.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Gate HAVE_UNISTD_H and HAVE_FCNTL_H behind a non-Windows check since
these headers do not exist with MSVC. Freetype includes zlib headers
which conditionally include unistd.h based on this define.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>