Fixes#9322
SwiftUI `Text` has huge performance issues. On my maxed out MBP it hangs
for any text more than 100KB (it took ~8s to display it!). `TextEditor`
with a constant value works much better and handles scrolling for us,
too!
Fixes#9132
We were processing our window size defaults separate from our window
position and the result was that you'd get some incorrect behavior.
Unify the logic more to fix the positioning.
Note there is room to improve this further, I think that all initial
positioning could go into the controller completely. But I wanted to
minimize the diff for a backport.
This adds a set of Wasm convenience functions to ease memory management.
These are all prefixed with `ghostty_wasm` and are documented as part of
the standard Doxygen docs.
I also added a very simple single-page HTML example that demonstrates
how to use the Wasm module for key encoding.
This also adds a bunch of safety checks to the C API to verify that
valid values are actually passed to the function. This is an easy to hit
bug.
**AI disclosure:** The example is AI-written with Amp. I read through
all the code and understand it but I can't claim there isn't a better
way, I'm far from a JS expert. It is simple and works currently though.
Happy to see improvements if anyone wants to contribute.
This makes `libghostty-vt` build for freestanding wasm targets (aka a
browser) and produce a `ghostty-vt.wasm` file. This exports the same C
API that libghostty-vt does.
This commit specifically makes the changes necessary for the build to
build properly and for us to run the build in CI. We don't yet actually
try using it...
Fixes a crash when NSLocale returns nil for languageCode or countryCode
properties. This can happen when the app launches without locale
environment variables set.
The crash occurs at `src/os/locale.zig:87-88` when trying to call
`getProperty()` on a nil object. The fix adds a null check and falls
back to `en_US.UTF-8` instead of dereferencing null.
## Testing
Tested by running with locale variables unset:
```bash
unset LC_ALL && ./zig-out/Ghostty.app/Contents/MacOS/ghostty
```
Before: segmentation fault
After: launches successfully with fallback locale
Fixes#8900
Our xterm modify other keys state 2 encoding was stripped consumed mods
from the keyboard event. This doesn't match xterm or other popular
terminal emulators (but most importantly: xterm). Use the full set of
mods and add a test to verify this.
Reproduction:
```
printf '\033[>4;2m'
cat
```
Then press `ctrl+shift+h` and compare across terminals.
Closes#8430
A few questions:
* Should I set a default keybind for `toggle-mouse-reporting`? The issue
mentioned one, it's currently unset.
* Am I handling the `toggle-mouse-reporting` action properly in
`performAction` (gtk) / `action` (macos)?
Copilot was used to understand the codebase, but code was authored
manually.
This cleans up some of our termio exec code by unifying process launch
state into a single union type. This makes it easier to distinguish
between the current two mutually exclusive modes of launching a process:
fork/exec and flatpak dbus commands.
It also ensures everyplace we touch related to process launching is
forced to address every case (exhaustive switch handling). I did find
one resource cleanup bug based on this cleanup, which is also fixed
here. This just improves memory slightly so it's not a big deal.
If we add future ways to launch processes, we can add a new union case.
For example, I originally had a `posix_spawn` option while I was
experimenting with that before abandoning it (see #9274).
The host validation code previously expected IPv6 addresses to be
enclosed in [brackets], but that's not how ssh(1) expects them.
This change removes that requirement and reimplements the host
validation routine to check for valid hostnames and IP addresses (IPv4
and IPv6) using standard routines rather than custom logic.
Fixes#9251
Bumps
[flatpak/flatpak-github-actions](https://github.com/flatpak/flatpak-github-actions)
from 6.5 to 6.6.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/flatpak/flatpak-github-actions/releases">flatpak/flatpak-github-actions's
releases</a>.</em></p>
<blockquote>
<h2>v6.6</h2>
<ul>
<li>Specify full URL policy when mirroring screenshots</li>
<li>Fix restore cache keys to actually include arch</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="92ae9851ad"><code>92ae985</code></a>
Fix restore cache keys to actually include arch</li>
<li><a
href="b8a638469e"><code>b8a6384</code></a>
Specify full URL policy when mirroring screenshots</li>
<li><a
href="6684584b07"><code>6684584</code></a>
Switch funding to flatpak instead</li>
<li><a
href="e5aa88fb51"><code>e5aa88f</code></a>
readme: Update flathub images list</li>
<li><a
href="b93832bada"><code>b93832b</code></a>
Sync readme from flathub's fork</li>
<li>See full diff in <a
href="10a3c29f01...92ae9851ad">compare
view</a></li>
</ul>
</details>
<br />
[](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)
Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.
[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)
---
<details>
<summary>Dependabot commands and options</summary>
<br />
You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
</details>
The host validation code previously expected IPv6 addresses to be
enclosed in [brackets], but that's not how ssh(1) expects them.
This change removes that requirement and reimplements the host
validation routine to check for valid hostnames and IP addresses (IPv4
and IPv6) using standard routines rather than custom logic.
std.net.isValidHostname is currently too generous. It considers strings
like ".example.com", "exa..mple.com", and "-example.com" to be valid
hostnames, which is incorrect according to RFC 1123 (the currently
accepted standard).
Until the standard library function is improved, we can use this local
implementation that does follow the RFC 1123 standard.
I asked Claude to perform an audit of the code based on its understand
of the RFC. It suggested some additional test cases and considers the
overall implementation to be robust (its words) and standards compliant.
Ref: https://www.rfc-editor.org/rfc/rfc1123
std.net.isValidHostname is currently too generous. It considers strings
like ".example.com", "exa..mple.com", and "-example.com" to be valid
hostnames, which is incorrect according to RFC 1123 (the currently
accepted standard).
Until the standard library function is improved, we can use this local
implementation that does follow the RFC 1123 standard.
I asked Claude to perform an audit of the code based on its understand
of the RFC. It suggested some additional test cases and considers the
overall implementation to be robust (its words) and standards compliant.
Ref: https://www.rfc-editor.org/rfc/rfc1123
`fish_add_path` by default updates the `fish_user_paths` universal
variable which makes the modification persist across shell sessions.
The integration also tries to update the `fish_user_paths` when the
desired path already appears in the `PATH` environment variable but not
in `fish_user_paths`. Because `fish_user_paths` will always be inserted
before the inherited `PATH` env. This makes the added path
unintentionally has a higher priority.
This patch fixes the above issues by adding `--global` and `--path`
options to `fish_user_paths` which limits the modification scope and
ensures that the path won't be added if it already exists in `PATH`.
Ref: https://fishshell.com/docs/current/cmds/fish_add_path.html
`fish_add_path` by default updates the `fish_user_paths` universal
variable which makes the modification persist across shell sessions.
The integration also tries to update the `fish_user_paths` when the
desired path already appears in the `PATH` environment variable but
not in `fish_user_paths`. Because `fish_user_paths` will always be
inserted before the inherited `PATH` env. This makes the added path
unintentionally has a higher priority.
This patch makes the above issues by adding `--global` and `--path`
options to `fish_user_paths` which limits the modification scope and
ensures that the path won't be added if it already exists in `PATH`.
This fixes the source of a deadlock that some tip users have hit. If our
surface mailbox is full and there is a dirty scrollbar state, then
drawFrame would block forever trying to queue to the surface mailbox.
We now fail instantly if the queue is full and keep the scrollbar state
dirty. We can try again on the next frame, it's not a critical thing to
get updated.
This fixes the source of a deadlock that some tip users have hit. If our
surface mailbox is full and there is a dirty scrollbar state, then
drawFrame would block forever trying to queue to the surface mailbox.
We now fail instantly if the queue is full and keep the scrollbar state
dirty. We can try again on the next frame, it's not a critical thing to
get updated.
NSScreen instances can be garbage collected at any time, even for
screens that remain connected, making NSMapTable with weak keys
unreliable for tracking per-screen state.
This changes the quick terminal to use CGDisplay UUIDs as stable
identifiers, keyed in a strong dictionary. Each entry stores the window
frame along with screen dimensions, scale factor, and last-seen
timestamp.
**This should make quick terminal size restore more stable than 1.2.2.**
Rules for pruning:
- Entries are invalidated when screens shrink or change scale
- Entries persist and update when screens grow (allowing cached state to
work with larger resolutions)
- Stale entries for disconnected screens expire after 14 days.
- Maximum of 10 screen entries to prevent unbounded growth
NSScreen instances can be garbage collected at any time, even for
screens that remain connected, making NSMapTable with weak keys
unreliable for tracking per-screen state.
This changes the quick terminal to use CGDisplay UUIDs as stable
identifiers, keyed in a strong dictionary. Each entry stores the
window frame along with screen dimensions, scale factor, and last-seen
timestamp.
Rules for pruning:
- Entries are invalidated when screens shrink or change scale
- Entries persist and update when screens grow (allowing cached state
to work with larger resolutions)
- Stale entries for disconnected screens expire after 14 days.
- Maximum of 10 screen entries to prevent unbounded growth
When the preferred scrollbar style is "legacy", the scrollbar takes up
space that offsets the actual terminal. To prevent reflow, we detect
this before the scrollbar becomes visible and shrink our terminal width
to prepare for it.
This doesn't account for the style changing at runtime, yet.
This changes the default FreeType load target to `FT_LOAD_TARGET_LIGHT`,
giving the hinter a lighter touch in line with the default behavior in
most other GTK apps, and adds a load flag such that the old hinting
behavior can be restored via config.
As discussed in
https://github.com/ghostty-org/ghostty/issues/8674#issuecomment-3417082534.
However, this doesn't close that issue, as it still doesn't respect
custom Fontconfig settings. It's just a stopgap solution bringing
Ghostty's defaults more in line with the typical results of not
customizing Fontconfig.
Fixes#2731 (again)
This regressed in 1.2 due to the renderer rework missing porting this. I
believe this issue is still valid even with the rework since the font
grid changes the atlas and if there are still cached cells that
reference the old atlas coordinates it will produce garbage.
Fixes#2731 (again)
This regressed in 1.2 due to the renderer rework missing porting this. I
believe this issue is still valid even with the rework since the font
grid changes the atlas and if there are still cached cells that
reference the old atlas coordinates it will produce garbage.
Fixes#111
This builds on the prior work and adds scrollbars to the GTK
application. After this PR, both the macOS and GTK applications support
scrollbars and #111 can be closed.
## TODO
- [x] Verify that with text coming out if we manually scroll to the
bottom we bind to the bottom
- [x] Valgrind checks
Fixes#9233. The issue was that the content view was effectively
truncated by `SurfaceScrollView`s safe area insets covering the titlebar
(I suppose the scroll view doesn't inherit the `ignoresSafeArea`
modifier on `TerminalSplitTreeView` because it's not actually a node in
that tree, it just contains a subview that is). I think it's OK to zero
these insets unconditionally, as I don't think the frame size passed
down from `SurfaceWrapper` will ever overlap with a visible titlebar.
This PR also fixes a somewhat related issue I discovered along the way.
In many cases, the mouse wouldn't work on the first row of text, whether
trying to select text or interact with TUI elements like neovim
bufferlines/tablines. When looking at the inspector while moving the
mouse around, you'd see the mouse position jump discontinuously from a
y-coordinate of 10-20 pixels to -3, i.e., no longer within the surface.
The problem turned out to be that the height of the document view only
included the text lines, not the window padding, while the content view
is sized to the surface including padding. If you imagine the
metaphorical sliding of the viewport up and down the document, and you
require the text to snap to a fixed grid on the viewport, it's clear
that the document must have the same top and bottom padding as the
viewport, otherwise the math breaks at the ends. Sure enough, adding
this padding fixed the problem.
To properly reproduce these issues and see the effect of the fixes, it's
helpful to change the system settings to always show scroll bars
(Appearance -> Show scroll bars -> Always). That's what connecting an
external mouse was all about. You should also remove the debug banner or
use a release build, otherwise the banner takes the place of the hidden
titlebar and conceals the issue.
Completes #111 for macOS
This builds on #9225 and adds the native, macOS GUI scrollbars for
terminals.
This doesn't do GTK, but all the groundwork is there to make this easy
on GTK, depending on how scrollviews work there. I have to look into
that still. But in theory all the information and controls we provide
out of core are generic to _any_ scrollbar drawing.
## Demo
https://github.com/user-attachments/assets/85683bf9-1117-4f32-aaec-d926edd68c39
## Details
- The scrollbars respect your macOS system settings on style, color,
visibility.
- A new configuration `scrollbar` controls whether scrollbars are
visible (defaults to `system` allowing the system to choose).
- There is a new keybind action `scroll_to_row:N` that lets you jump to
an absolute row number N. This is implemented efficiently. This is how
grabbing the knob and scrolling works.