This sets up for a couple improvments (see TODO comments) and also sets
the glyph atlas textures to nearest neighbor sampling since we can do
that now that we never scale glyphs.
This is a big'un.
- **Glyph constraint logic is now done fully on the CPU** at the
rasterization stage, so it only needs to be done once per glyph instead
of every frame. This also lets us eliminate padding between glyphs on
the atlas because we're doing nearest-neighbor sampling instead of
interpolating-- which ever so slightly increases our packing efficiency.
- **Special constraints for nerd font glyphs** are applied based roughly
on the constraints they use in their patcher. It's a simplification of
what they do, the largest difference being that they scale groups of
glyphs based on a shared bounding box so that they maintain relative
size to one another, but that would require loading all glyphs on the
group and I'd want to do that on font load TBH and at that point I'd
basically be re-implementing the nerd fonts patcher in Zig to patch
fonts at load time which is way beyond the scope I want to have. (Fixes
#7069)
- These constraints allow for **perfectly sized and centered emojis**,
this is very nice.
- **Changed the default embedded fonts** from 4 copies (regular, italic,
bold, bold italic) of a patched (and outdated) JetBrains Mono to a
single JetBrains Mono variable font and a single Nerd Fonts Symbols Only
font. This cuts the weight of those down from 9MB to 3MB!
- **FreeType's `renderGlyph` is significantly reworked**, and the new
code is, IMO, much cleaner- although there are probably some edge case
behavior differences I've introduced.
> [!NOTE]
> One breaking change I definitely introduced is changing the
`monochrome` freetype load flag config from its previous completely
backwards meaning to instead the correct one (I also changed the
default, so this won't affect any user who hasn't touched it, but users
who set the `monochrome` flag will find their fonts quite crispy after
this change because they will have no anti-aliasing anymore)
### Future work
Following this change I want to get to work on automatic font size
matching (a la CSS
[`font-size-adjust`](https://developer.mozilla.org/en-US/docs/Web/CSS/font-size-adjust)).
I set the stage for that quite some time ago so it shouldn't be too much
work and it will be a big benefit for users who regularly use multiple
writing systems and so have multiple fonts for them that aren't
necessarily size-compatible.
This is in preparation to move constraint off the GPU to simplify our
shaders, instead we only need to constrain once at raster time and never
again.
This also significantly reworks the freetype renderGlyph function to be
generally much cleaner and more straightforward.
This commit doesn't actually apply the constraints to anything yet, that
will be in following commits.
This deletes the GLFW apprt from the Ghostty codebase.
The GLFW apprt was the original apprt used by Ghostty (well, before
Ghostty even had the concept of an "apprt" -- it was all just a single
application then). It let me iterate on the core terminal features,
rendering, etc. without bothering about the UI. It was a good way to get
started. But it has long since outlived its usefulness.
We've had a stable GTK apprt for Linux (and Windows via WSL) and a
native macOS app via libghostty for awhile now. The GLFW apprt only
remained within the tree for a few reasons:
1. Primarily, it provided a faster feedback loop on macOS because
building the macOS app historically required us to hop out of the
zig build system and into Xcode, which is slow and cumbersome.
2. It was a convenient way to narrow whether a bug was in the
core Ghostty codebase or in the apprt itself. If a bug was in both
the glfw and macOS app then it was likely in the core.
3. It provided us a way on macOS to test OpenGL.
All of these reasons are no longer valid. Respectively:
1. Our Zig build scripts now execute the `xcodebuild` CLI directly and
can open the resulting app, stream logs, etc. This is the same
experience we have on Linux. (Xcode has always been a dependency of
building on macOS in general, so this is not cumbersome.)
2. We have a healthy group of maintainers, many of which have access
to both macOS and Linux, so we can quickly narrow down bugs
regardless of the apprt.
3. Our OpenGL renderer hasn't been compatible with macOS for some time
now, so this is no longer a useful feature.
At this point, the GLFW apprt is just a burden. It adds complexity
across the board, and some people try to run Ghostty with it in the real
world and get confused when it doesn't work (it's always been lacking in
features and buggy compared to the other apprts).
So, it's time to say goodbye. Its bittersweet because it is a big part
of Ghostty's history, but we've grown up now and it's time to move on.
Thank you, goodbye.
(NOTE: If you are a user of the GLFW apprt, then please fork the project
prior to this commit or start a new project based on it. We've warned
against using it for a very, very long time now.)
Supersedes #7154
In gtk4-layer-shell versions < 1.0.4, the app could crash upon opening
a quick terminal window on certain compositors that implement the
`xdg_wm_dialog_v1` protocol. The exact reason is a bit complicated,
but is nicely summarized in the upstream issue (wmww/gtk4-layer-shell#50).
The circumstances that could cause this crash to occur should gradually
diminish as distros update to newer gtk4-layer-shell versions, but this
is known to crash on Fedora 41 and Hyprland, which could be a sizable
chunk of our userbase given that this would also occur on GNOME/Mutter
and KDE/KWin. The diff should be minimal enough that this can be removed
or reverted once this band-aid fix is no longer necessary.
BPTC is required to be available OpenGL >= 4.2 and our minimum is 4.3 so
this is safe in terms of support. I tested briefly in a VM and didn't
encounter any problems so this should just be a complete win.
(Note: texture data is already automatically compressed on Metal)
The code in metal/image.zig and opengl/image.zig was virtually identical
save for the texture options, so I've moved that to the GraphicsAPI and
unified them in to renderer/image.zig
It's here, the long-foretold and long-procrastinated renderer rework!
Hopefully this makes it easier to adapt and modify the renderer in the
future and ensures feature parity between Metal and OpenGL. Despite
having been a lot of work to write initially, with the abstraction layer
in place I feel like working on the renderer will be a much more
pleasant experience going forward.
## Key points
- CPU-side renderer logic is now mostly unified via a generic
`Renderer`.
- A graphics API abstraction layer over OpenGL and Metal has been
introduced.
- Minimum OpenGL version bumped to `4.3`, so can no longer be run on
macOS; I used the nix VM stuff for my testing during development. (Edit
by @mitchellh: Note for readers that Ghostty still works on macOS, but
the OpenGL backend doesn't, only the Metal one)
- The OpenGL backend now supports linear blending! Woohoo! The default
`alpha-blending` has been updated to `linear-corrected` since it's
essentially a strict improvement over `native`. The default on macOS is
still `native` though to match other mac apps in appearance, since macOS
users are more sensitive to text appearance.
- Custom shaders can now be hot reloaded.
- The background color is once again drawn by us, so custom shaders can
interact with it properly. In general, custom shaders should be a little
more robust.
## The abstraction layer
The general hierarchy of the abstraction layer is as such:
```
[ GraphicsAPI ] - Responsible for configuring the runtime surface
| | and providing render `Target`s that draw to it,
| | as well as `Frame`s and `Pipeline`s.
| V
| [ Target ] - Represents an abstract target for rendering, which
| could be a surface directly but is also used as an
| abstraction for off-screen frame buffers.
V
[ Frame ] - Represents the context for drawing a given frame,
| provides `RenderPass`es for issuing draw commands
| to, and reports the frame health when complete.
V
[ RenderPass ] - Represents a render pass in a frame, consisting of
: one or more `Step`s applied to the same target(s),
[ Step ] - - - - each describing the input buffers and textures and
: the vertex/fragment functions and geometry to use.
:_ _ _ _ _ _ _ _ _ _/
v
[ Pipeline ] - Describes a vertex and fragment function to be used
for a `Step`; the `GraphicsAPI` is responsible for
these and they should be constructed and cached
ahead of time.
[ Buffer ] - An abstraction over a GPU buffer.
[ Texture ] - An abstraction over a GPU texture.
```
More specific documentation can be found on the relevant structures.
## Miscellany
Renderers (which effectively just means the generic renderer) are now
expected to only touch GPU resources in `init`, certain lifecycle
functions such as the `displayRealized`/`displayUnrealized` callbacks
from GTK-- and `drawFrame`; and are also expected to be thread-safe.
This allows the renderer thread to build the CPU-side buffers
(`updateFrame`) even if we can only *draw* from the app thread.
Because of this change, we can draw synchronously from the main thread
on macOS when necessary to always have a frame of the correct size
during a resize animation. This was necessary to allow the background to
be drawn by our GPU code (instead of setting a background color on the
layer) while still avoiding holes during resize.
The OpenGL backend now theoretically has access to multi-buffering, but
it's disabled (by setting the buffer count to 1) because it
synchronously waits for frames to complete anyway which means that the
extra buffers were just a waste of memory.
## Validation
To validate that there are no significant or obvious problems, I
exercised both backends with a variety of configurations, and visually
inspected the results. Everything looks to be in order.
The images are available in a gist here:
https://gist.github.com/qwerasd205/c1bd3e4c694d888e41612e53c0560179
## Memory
Here's a comparison of memory usage for ReleaseFast builds on macOS,
between `main` and this branch.
Memory figures given are values from Activity Monitor measuring windows
of the same size, with two tabs with 3 splits each.
||Before|After|
|-:|-|-|
|**Memory**|247.9 MB|224.2 MB|
|**Real Memory**|174.4 MB|172.5 MB|
Happily, the rework has slightly *reduced* the memory footprint- likely
due to removing the overhead of `CAMetalLayer`. (The footprint could be
reduced much further if we got rid of multi-buffering and satisfied
ourselves with blocking for each frame, but that's a discussion for
another day.)
If someone could do a similar comparison for Linux, that'd be much
appreciated!
## Notes / future work
- There are a couple structures that *can* be unified using the
abstraction layer, but I haven't gotten around to unifying yet.
Specifically, in `renderer/(opengl|metal)/`, there's `cell.zig` and
`image.zig`, both of which are substantially identical between the two
backends. `shaders.zig` may also be a candidate for unification, but
that might be *overly* DRY.
- ~~I did not double-check the documentation for config options, which
may mention whether certain options can be hot-reloaded; if it does then
that will need to be updated.~~ Fixed: be5908f
- The `fragCoord` for custom shaders originates at the top left for
Metal, but *bottom* left for OpenGL; fixing this will be a bit annoying,
since the screen texture is likewise vertically flipped between the two.
Some shaders rely on the fragcoord for things like falling particles, so
this does need to be fixed.
- `Target` should be improved to support multiple types of targets right
now it only represents a framebuffer or iosurface, but it should also be
able to represent a texture; right now a kind of messy tagged union is
used so that steps can accept both.
- Custom shader cursor uniforms (#6912) and terminal background images
(#4226, #5233) should be much more straightforward to implement on top
of this rework, and I plan to make follow-up PRs for them once this is
merged.
- I *do* want to do a rework of the pipelines themselves, since the way
we're rendering stuff is a bit messy currently, but this is already a
huge enough PR as it is- so for now the renderer still uses the same
rendering passes that Metal did before.
- We should probably add a system requirements section to the README
where we can note the minimum required OpenGL version of `4.3`, any even
slightly modern Linux system will support this, but it would be good to
document it somewhere user-facing anyway.
# TODO BEFORE MERGE
- [x] Have multiple people test this on both macOS and linux.
- [ ] ~~Have someone with a better dev setup on linux check for memory
leaks and other problems.~~ (Skipped, will merge and let tip users
figure this out, someone should *specifically* look for memory leaks
before the next versioned release though.)
- [x] Address any code review feedback.
This commit is very large, representing about a month of work with many
interdependent changes that don't separate cleanly in to atomic commits.
The main change here is unifying the renderer logic to a single generic
renderer, implemented on top of an abstraction layer over OpenGL/Metal.
I'll write a more complete summary of the changes in the description of
the PR.
Updates all copyright notices to include "Ghostty contributors" to
reflect the fact that Mitchell is not the sole copyright owner.
Also adds "Ghostty contributors" to the author section in the manpages,
linking https://github.com/ghostty-org/ghostty/graphs/contributors for
proper credit.
Even though gtk4-layer-shell's documentation claims that "nobody quite
knows what it's for", some compositors (like Niri) can define custom
rules based on the layer name and it's beneficial in those cases to
define a distinct name just for our quick terminals.
This fixes an issue where Ghostty would not build against the macOS 15.5 SDK.
What was happening was that Zig was adding its embedded libc paths to
the clang command line, which included old headers that were
incompatible with the latest (macOS 15.5) SDK. Ghostty was adding the
newer paths but they were being overridden by the embedded libc paths.
The reason this was happening is because Zig was using its own logic to
find the libc paths and this was colliding with the paths we were
setting manually. To fix this, we now use a `libc.txt` file that
explicitly tells Zig where to find libc, and we base this on our own SDK
search logic.
This commit changes a LOT of areas of the code to use decl literals
instead of redundantly referring to the type.
These changes were mostly driven by some regex searches and then manual
adjustment on a case-by-case basis.
I almost certainly missed quite a few places where decl literals could
be used, but this is a good first step in converting things, and other
instances can be addressed when they're discovered.
I tested GLFW+Metal and building the framework on macOS and tested a GTK
build on Linux, so I'm 99% sure I didn't introduce any syntax errors or
other problems with this. (fingers crossed)
This updates our bundled Harfbuzz from 8.4 to 11.0. The changes from 8
to 11 include a number of correctness and performance improvements.
Packaged releases tend to dynamically link so this won't affect
existing users, but build-from-source users hopefully get an
improvement.
We have always tracked a post-3.4 release, this brings us up to date.
There's no real motivation beyond this other than keeping up to date
since we're already on non-release versions anyways.
We don't currently support rendering SVG glyphs so they should be
ignored when loading. Additionally, the check for whether a glyph is
colored has been simplified by just checking the pixel mode of the
rendered bitmap.
This commit also fixes a bug caused by calling the color check inside of
`renderGlyph`, which caused the bitmap to be freed creating a chance for
memory corruption and garbled glyphs.
Fixes#6727
The major change in this commit is to consolidate all the C imports in
a single decl in main.zig. This is required for Zig 0.14. Without it,
the problem in #6727 will happen. I was never able to minimize why this
happens in order to open a Zig bug.
Beyond this, I fixed the build.zig and build.zig.zon to work with Zig
0.14 so that we can test building `pkg/macos` in isolation. There are no
downstream impacting changes in the build.zig files.
Lazy dependencies are only fetched if the build script would actually
reach a usage of that dependency at runtime (when the `lazyDependency`
function is called). This can save a lot of network traffic, disk uage,
and time because we don't have to fetch and build dependencies that we
don't actually need.
Prior to this commit, Ghostty fetched almost everything for all
platforms and configurations all the time. This commit reverses that to
fetching almost nothing until it's actually needed.
There are very little downsides to doing this[1]. One downside is `zig
build --fetch` doesn't fetch lazy dependencies, but we don't rely on
this command for packaging and suggest using our custom shell script
that downloads a cached list of URLs (`build.zig.zon.txt`).
This commit doesn't cover 100% of dependencies, since some provide no
benefit to make lazy while the complexity to make them lazy is higher
(in code style typically).
Conversely, some simple dependencies are marked lazy even if they're
almost always needed if they don't introduce any real complexity to the
code, because there is very little downside to do so.
[1]: https://ziggit.dev/t/lazy-dependencies-best-dependencies/5509/5
Closes#6702
This removes our mach-glfw dependency and replaces it with an in-tree
pkg/glfw that includes both the source for compiling glfw as well as the
Zig bindings. This matches the pattern from our other packages.
This is based on the upstream mach-glfw work and therefore includes the
original license and copyright information.
The reasoning is stated in the issue but to summarize for the commit:
- mach-glfw is no longer maintained, so we have to take ownership
- mach-glfw depended on some large blobs of header files to enable
cross-compilation but this isn't something we actually care about,
so we can (and do) drop the blobs
- mach-glfw blobs were hosted on mach hosts. given mach-glfw is
unmaintained, we can't rely on this hosting
- mach-glfw relied on a "glfw" package which was owned by another
person to be Zig 0.14 compatible, but we no longer need to rely on
this
- mach-glfw builds were outdated based on latest Zig practices
By linking using the pkg-config name we gain the compiler flags in pkgconf
for linking, specifically the -I <headers> to include system-installed
headers. This allows the gtk4-layer-shell pkg to not require the source
files specified in the `pkg/gtk4-layer-shell/build.zig.zon`.
pkg(gtk4-layer-shell): Refactor to allow dynamic linking
Refactored `pkg/gtk4-layer-shell/build.zig` to have similar structure
to `pkg/oniguruma/build.zig`.
Now dynamic link using pkgconfig, this adds pkgconfig compiler flags.
So we are now using system-installed headers to resolve @cInclude().
As of now `gtk4-layer-shell` is unavailable on recent, stable releases
of many distros (Debian 12, Ubuntu 24.04, openSUSE Leap & Tumbleweed, etc.)
and outdated on many others (Nixpkgs 24.11/unstable, Fedora 41, etc.)
This is inconvenient for our users and severely limits where the quick
terminal can be used. As a result we then build gtk4-layer-shell ourselves
by default unless `--system` or `-fsys=gtk4-layer-shell` are specified.
This also allows me to add an idiomatic Zig API on top of the library
and avoiding adding even more raw C code in the GTK apprt.
Since we now build gtk4-layer-shell it should be theoretically available
on all Linux systems we target. As such, the `-Dgtk-layer-shell` build
option has been removed. This is somewhat of an experimental change as
I don't know if gtk4-layer-shell works perfectly across all distros, and
we can always add the option back if need be.
This adds a new script `update-mirror.sh` which generates the proper
blob format for R2 (or any blob storage) to mirror all of our
dependencies.
It doesn't automate updating build.zig.zon but on an ongoing basis this
should be easy to do manually, and we can strive to automate it in the
future.
I omitted iTerm2 color themes because we auto-update that via CI and
updating all of the machinery to send it to our mirror and so on is a
pain. Additionally, this doesn't mirror transitive dependencies because
Zig doesn't have a way to fetch those from a mirror instead (unless you
pre-generate a full cache like packagers but that's not practical for
day to day development).
It's hugely beneficial just to get most of our dependencies mirrored.
This fixes a regression in 1.1.1/1.1.2 where our PACKAGING docs mention
using `fetch-zig-cache.sh` but it was removed. This commit adds it back,
generating its contents from the build.zig.zon file (via zon2nix which
we use for our Nix packaging).
For packagers, there are no dependency changes: you still need Zig and
POSIX sh. For release time, Ghostty has a new dependency on `jq` but
otherwise the release process is the same. The check-zig-cache.sh script
is updated to generate the new build.zig.zon.txt file.
NEEDS REVIEW
continuation of #5037resolves#4729
renders all shaders to the default buffer and then copies it to the
designated custom shader texture.
this is a draft pr because:
- it introduces a new shader "pipeline" which doesnt fit in with how the
system was designed to work (which is only rendering to the fbo)
- im not sure if this is the best way to achieve shaders being able to
sample their output while also drawing to the screen. the cusom fbo
(previous implementation) was useful in that it modularized the custom
shader stage in rendering
---------
Co-authored-by: Mitchell Hashimoto <m@mitchellh.com>
It seems like the raw data version of the kitty graphics transmit
operation is meant to be unassociated (aka straight) alpha, though I
can't find any definitive documentation either way- but in any case
unassociated alpha is more common in image formats and makes the
handling easier for the rest of it.
Also removed a redundant call to `decode_frame_config`, since it's
called implicitly when we call `decode_frame` right after.
This commit is quite large because it's fairly interconnected and can't
be split up in a logical way. The main part of this commit is that alpha
blending is now always done in the Display P3 color space, and depending
on the configured `window-colorspace` colors will be converted from sRGB
or assumed to already be Display P3 colors. In addition, a config option
`text-blending` has been added which allows the user to configure linear
blending (AKA "gamma correction"). Linear alpha blending also applies to
images and makes custom shaders receive linear colors rather than sRGB.
In addition, an experimental option has been added which corrects linear
blending's tendency to make dark text look too thin and bright text look
too thick. Essentially it's a correction curve on the alpha channel that
depends on the luminance of the glyph being drawn.