Fixes#10282
The function `cursorChangePin` is supposed to be called anytime the
cursor page pin changes, but it itself may alter the page pin if setting
up the underlying managed memory forces a page size adjustment.
Multiple callers to this function were erroneously reusing the old page
pin value.
**AI disclosure:** I had Amp help me write the test. I eyeballed and
found the bug myself, verified it by asking Amp to write the test,
reviewed that manually, then implemented the fixes manually and got it
to pass.
Fixes#10282
The function `cursorChangePin` is supposed to be called anytime the
cursor page pin changes, but it itself may alter the page pin if setting
up the underlying managed memory forces a page size adjustment.
Multiple callers to this function were erroneously reusing the old page
pin value.
This simplifies our CI command line and makes it easier to document
expected usage (in HACKING.md).
There unfortunately isn't a way to set --checked-sourced or our default
warning level in .shellcheckrc, and our `find` command is still a bit
unwieldy, but this is still a net improvement.
This simplifies our CI command line and makes it easier to document
expected usage (in HACKING.md).
There unfortunately isn't a way to set --checked-sourced or our default
warning level in .shellcheckrc, and our `find` command is still a bit
unwieldy, but this is still a net improvement.
## Summary
- Fix literal \n appearing in window titles when running commands in zsh
## Description
The zsh shell integration was using ${(V)1} parameter expansion to set
the window title. The (V) flag converts control characters to their
visible escape sequence representations, causing commands ending with a
newline to display as command\n in the title bar.
Changed to use ${1//[[:cntrl:]]} which strips control characters
entirely, matching the behavior of the bash integration.
<img width="231" height="47" alt="image"
src="https://github.com/user-attachments/assets/356c77a1-32da-47b0-bf86-29735cc67e90"
/>
## Test
- Open Ghostty with zsh shell integration enabled
- Run a command (e.g., cargo install trunk)
---
AI Disclosure: This PR was written primarily by Claude Code. (Opus 4.5)
The zsh shell integration was using `${(V)1}` parameter expansion to set
the window title, which converts control characters to their visible
escape sequence representations. This caused commands ending with a
newline to display as `command\n` in the title bar.
Changed to use `${1//[[:cntrl:]]}` which strips control characters
entirely, matching the behavior of the bash integration.
Bumps
[namespacelabs/nscloud-setup-buildx-action](https://github.com/namespacelabs/nscloud-setup-buildx-action)
from 0.0.20 to 0.0.21.
<details>
<summary>Commits</summary>
<ul>
<li><a
href="a7e5254161"><code>a7e5254</code></a>
Adopt new wait command in action. (<a
href="https://redirect.github.com/namespacelabs/nscloud-setup-buildx-action/issues/12">#12</a>)</li>
<li>See full diff in <a
href="91c2e65377...a7e5254161">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>
Assuming /bin/sh is symlinked to bash, the handling of special builtin
'source' is slightly different between bash and bash-in-POSIX-mode (as a
result of being invoked through /bin/sh). Specifically errors in builtin
'source' cannot be masked with `|| true`. Compare
```
$ ls -l /bin/sh
lrwxrwxrwx 1 root root 4 Dec 11 11:00 /bin/sh -> bash
$ /bin/sh -c 'set -e ; source nofile || true; echo ok'
/bin/sh: line 1: source: nofile: file not found
$ /bin/bash -c 'set -e ; source nofile || true; echo ok'
/bin/bash: line 1: nofile: No such file or directory
ok
```
Thus ghostty from snap would not start at all when
$SNAP_USER_DATA/.last_revision does not exist causign the launcher
script to exit prematurely.
Clicking on the icon immediately advances to the next one. Hovering on
the icon pauses the automatic cycling, and the "help" tooltip displays
the icon's configuration name (for `macos-icon`).
This fixes#10265 and thus also the remaining part of #9718 and likely
#10250.
The issue was that when using `insertLines` and `deleteLines` to
generate scrolling in a region that spans a page boundary, rows that are
replaced by a row from a different page lose their dirty flags in the
clone operation, since the flag is part of the data that gets cloned.
The solution is to set the dirty flag again after the clone, just like
the non-cloning branch does after the pointer swap.
**AI disclosure:** Amp is the MVP here. I prompted it with the
hypothesis I developed in #10265 (that this happens when the scrolling
region spans a page boundary), supplemented with insight I gained from
perusing asciicast files (that the offending scrolling operations are
always triggered by `CSI 1 L` or `CSI 1 M`, that is,
`Terminal.insertLines` or `Terminal.deleteLines`). Amp figured out the
rest and drafted the fix and tests. For free!
I cleaned up the tests and then pushed back a bit against the logic
behind the fix, which led to a better understanding and what I think is
a more appropriate fix. I can explain the details if there's interest,
or people can just skim the thread here:
https://ampcode.com/threads/T-019bb0d6-5334-744a-b78a-7c997ec1fade.
As of NixOS/nixpkgs#473413, `zig.hook` no longer supports
`zig_default_flags`, and now they can and must be provided in
`zigBuildFlags` instead.
Updating also requires removing gnome-xorg since it has been removed
from nixpkgs.
`nix flake check` succeeds on my system (x86_64-linux), with a couple
deprecation warnings that I believe aren't important.
Does not entail any actual changes in the version, merely in the comment
indicating the used version.
Detected by CI (GitHub Action Pins) after nixpkgs update.
I've been poking around our internals around overflow cases while
diagnosing systemic ways to fix#10258.
One thing I found is that our terminal internals advertise that they can
accept up to u16 (~65K) rows and columns for the active area, but at
around 46K columns on x86_64 and 47K on aarch64, our terminal initialize
would error out. Worse, we'd actually trigger integer overflow and crash
in safe builds (and have silent corruption in release builds).
In fixing this, I realized we could comptime-verify a bunch of things
and avoid overflows completely. So this diff contains a bunch of places
that previously had error returns and now do not! 😄 This is a bit silly,
but with libghostty it's more relevant to have correctness around the
edges.
We now accept full u16 rows/cols. More than that isn't allowed by the
type system and our upstream callers must handle that appropriately.
cc @qwerasd205 since its relevant to layout fallibility but not on the
same path
As of NixOS/nixpkgs#473413[1], `zig.hook` no longer supports
`zig_default_flags`, and now they can and must be provided in
`zigBuildFlags` instead.
Updating also requires removing gnome-xorg since it has been removed
from nixpkgs.
[1] https://github.com/NixOS/nixpkgs/pull/473413
`+help` and `+version` is missing from completions, even though they are
working actions and are referenced in discussion template
[here](https://github.com/ghostty-org/ghostty/pull/7012). This PR adds
the completion for fish (as that is the shell that I use).
This was an unused codepath and it complicates some things I'd like to
do, such as resetting our pools during resize. It complicates those
paths because if a user provides a pool we can't reset it (because other
things might be in it). It's best to own the pools. And since we didn't
reuse pools anyway, let's remove that.
Note this was previously used by our old render state mechanism (before
`terminal.RenderState`) as a way for the renderer to speed up clones by
having a preheated pool that was likely the right size before every
frame. Since we changed methods, we don't need it.
Fixes: #10196
If user invokes `search_selection` the contents of that string are no
longer ignored.
I'm not very familar with how apprt actions are sent to macos to ensure
the defer free will work there, but it is required for GTK otherwise we
are leaking the memory
I'm also open to making the string optional instead of checking against
`""` thats just how the current `start_search` action was sending its
needle (which I assume is better for macos?)
The text is also highlighted currently due to `grabFocus` always
selecting the search contents, not sure how the macos part works but
that could easily be changed to change the contents after focus was
grabbed
https://github.com/ghostty-org/ghostty/blob/main/src/apprt/gtk/class/search_overlay.zig#L233-L241
This _finally_ resolves#9962 (and the myriad of dupes).
The core issue was that when a non-standard size page is reused as part
of our scrollback pruning, it was resized to be standard size, which
caused our future frees to believe it was pooled and not call `munmap`
properly.
The solution I chose was to never reuse non-standard sized pages. If
during scrollback pruning we detect a non-standard page, we destroy it
and re-alloc. This frees the old memory and reuses pool memory for the
new page.
As part of this, I also introduced a custom page allocator that uses
macOS's mach kernel virtual memory tagging feature to specifically tag
our PageList memory. I was able to use this in conjunction with
Instruments and `footprint` to verify that our PageList memory was
previously not freed and is now successfully freed.
**No AI was used in my work here.** AI was used by others in their
analysis of this issue that I used the results of to help guide me and
give me other things to consider, but the ultimate understanding and fix
was all done via my own meat sticks.
## Detailed Explainer
Ghostty uses a memory pool of fixed-size `mmap`-ed pages to serve as the
backing memory for our terminal. If the terminal requires a non-standard
(larger) amount of memory due to an abundance of emoji, styles,
hyperlinks, etc. then we allocate non-pooled pages directly with `mmap`.
When freeing our pages, if it is `<= standard size` we just put it back
into the memory pool. If it is larger, we `munmap` it.
This explains and defines both a _standard_ and therefore _non-standard_
page.
Ghostty also has the concept of a "scrollback limit" (exposed to the
user via the `scrollback-limit` config). This caps the size of our
scrollback or history. When we reach this limit, we have a trick that we
do: to avoid allocation, we reuse the oldest page in the history.
Unfortunately, as part of this process, we were resizing the underlying
memory to be standard size again. This was causing our future frees to
believe this was pooled memory, and never unmap it. This was the main
source of the leak.
## Thanks
Big shout out to @grishy for being the person that finally got me a
reproduction so I could analyze the issue for myself. His own analysis
got to the same conclusion as me but thanks to the reproduction I was
able to verify both our understandings independently.