With zsh, when installing the ghostty terminfo on a server via the
ssh-terminfo shell integration, parts of the terminfo get mangled. In
particular, the newline escape sequence in
```
> infocmp -0 -x xterm-ghostty | grep ind=
...,ind=\n,indn=...
```
gets interpreted by `print` as a literal newline, which then just gets ignored / does not have the intended effect.
Documentation for the `-r` flag of `print` used in the fix is [here](https://zsh.sourceforge.io/Doc/Release/Shell-Builtin-Commands.html#:~:text=Ignore%20the%20escape%20conventions%20of%20echo.).
### Testing locally
You can directly demonstrate this locally. This outputs a host of warning messages:
```
ssh_terminfo=$(infocmp -0 -x xterm-ghostty 2>/dev/null)
print "$ssh_terminfo" | tic -x -
```
Whereas
```print -r "$ssh_terminfo" | tic -x -```
or
```infocmp -0 -x xterm-ghostty | tic -x -```
work without issue.
### Testing remotely
The most visible way is to observe the output of `htop` before and after the change.
More directly, the output of `infocmp -x xterm-ghostty | grep " ind="` should be
```ich=\E[%p1%d@, ich1=\E[@, il=\E[%p1%dL, il1=\E[L, ind=\n,```
instead of
```ich=\E[%p1%d@, ich1=\E[@, il=\E[%p1%dL, il1=\E[L, ind=,```
---
Discussed in #11031.
---
AI disclosure: I used Claude for parts of figuring out what was going on. The fix itself and the rest was written and tested by myself.
The C struct Palette.C declared colors as [265]Color.C, but the
terminal palette is 256 colors (terminal.color.Palette = [256]RGB)
and the C header ghostty_config_palette_s correctly uses colors[256].
The mismatch causes ghostty_config_get to write 265×3 = 795 bytes
through a pointer sized for 256×3 = 768 bytes, producing a 27-byte
buffer overflow. On macOS Release builds with stack protector enabled,
this triggers __stack_chk_fail → SIGABRT on launch.
Fixes#10406
ImGui_ImplOpenGL3_Shutdown() calls imgl3wShutdown() which dlcloses the
GL library handles but does not zero out the imgl3w function pointer
table (imgl3wProcs). When a GLArea is re-realized (e.g. during
reparenting), ImGui_ImplOpenGL3_Init() calls ImGui_ImplOpenGL3_InitLoader()
which checks "if (glGetIntegerv == nullptr)". Since the stale pointers
are non-null, it skips re-initialization. The next GL call through a
dangling function pointer causes a SIGSEGV.
Fix this by introducing ImGui_ImplOpenGL3_ShutdownWithLoaderCleanup()
which calls the normal shutdown and then zeroes the imgl3wProcs table,
forcing the next Init to reload GL function pointers via imgl3wInit().
Also properly destroy the ImGui context and reset widget state in
glAreaUnrealize so re-realize starts clean. This was extra but was
probably leaking memory.
Specifically:
iCurrentCursorStyle
iPreviousCursorStyle
iCurrentCursorVisible
iPreviousCursorVisible
Visibility calculated and updated independently from the typical cursor
unifrom updates to preserve cursor style even when not in the viewport
or set to be hidden
The PR introduces `lib-vt` Android support as discussed in #10902.
A few more notes:
- Introduces new CI for Android builds as a change requires NDK to be
configured.
- To build locally, it is required to have the NDK installed in the
system and either have the path exported via `ANDROID_NDK_HOME` pointing
to the exact NDK path or `ANDROID_HOME` or `ANDROID_SDK_ROOT` pointing
at the Android SDK path from which the build system will infer the NDK
path and version.
- 16kb page size alignment is configured for Android 15+. Builds are
backward compatible with 4kb page size devices.
Fixes#10548
Escaped characters in selection-word-chars are now correctly parsed,
allowing for characters like `\t` to be included in the set of word
characters.
Fixes#10680
The image state is used for drawing, so when we update it, we need to
acquire the draw mutex. All our other state updates already acquire the
draw mutex but Kitty images are odd in that they happen in the critical
area (due to their size).
I mostly did this to familiarize myself with the codebase and figured it
doesn't hurt to cover this with tests if more added logic in this area,
despite this logic receiving indirect coverage elsewhere. [Here's my
related
proposal](https://github.com/ghostty-org/ghostty/discussions/10807).
I gave more thought around how to expose some of these config values and
their metadata in the C api to eventually drive a settings UI and was
hoping for feedback before I proceed.
The cleanest path forward feels like annotating config values with
formal metadata around things like: supported platforms, whether or not
a restart is required, presentation metadata like grouping + ordering,
tolerated ranges for values, possible enum values, etc. My intent is
that Swift & other consumers can enumerate potential settings values
with metadata such as to drive the UI from the metadata.
---
AI Disclosure: I used Codex 5.3 to help me understand how the config
subsystem in zig is exposed to Swift via the C API. Codex wrote these
tests; but we brainstormed on a pragmatic coverage balance and I
understand how the tests work.
I mostly did this to familiarize myself with the codebase and figured it
doesn't hurt to cover this with tests if I add more in this area,
despite receiving indirect coverage elsewhere.
From #10554
> I can see there being space for some kind of new sequence that tells
the terminal that "hey, I want a semantic palette" or something, that is
better adjusted to themes automatically. But, I don't think we should
this by default.
> So my concrete proposal is to eliminate the inversion and bg => fg
ramp and do a more typical dark => light ramp (still taking into account
bg/fg, but keeping 16 black and 231 white). I think this is the only way
we can really make this a default on feature. I think this would address
all the negative reactions we've gotten to it.
This adds a new `palette-harmonious`, disabled by default, which allows
generated light themes to be inverted.
Implements the `output` option for the `scroll-to-bottom` configuration,
which scrolls the viewport to the bottom when new lines are printed.
Co-Authored-By: Sachin <sachinbeniwal0101@gmail.com>
The space-segment patterns in the path regex (dotted_path_space_segments
and any_path_space_segments) greedily consume text after a space even
when we know that the text is the start of a new independent path (e.g.,
`/tmp/bar` in `/tmp/foo /tmp/bar`).
Fix: Add two negative lookaheads after the space in both patterns:
- `(?!\.{0,2}\/)` → don't match if the next segment starts with `/`,
`./`, or `../`
- `(?!~\/)` → don't match if the next segment starts with `~/`
Fixes#8406
Spatial split navigation now wraps at the edges.
We first attempt the nearest spatial target using the existing slot geometry.
If there is no candidate in the requested direction, we synthesize a wrapped
target by shifting the current slot by one full grid in the opposite
direction and reuse the same nearest-distance logic.
This fake target works because the grid is 1x1, so by moving it a full
grid size in the opposite direction, we effectively wrap around to the
other side of the grid.
Closes#9266.
Big Note: I noticed that this worked properly under `NixOS`, but on my
`Ubuntu` VM it didnt.
The reason is in
[src/build/GhosttyI18n.zig](73a93abf7b/src/build/GhosttyI18n.zig (L24-L31))
because the locale is expected in the `<lang>_<region>` without the
encoding suffix. `<lang>_<region>_<encoding>`
```
// There is no encoding suffix in the LC_MESSAGES path on FreeBSD,
// so we need to remove it from `locale` to have a correct destination string.
// (/usr/local/share/locale/en_AU/LC_MESSAGES)
const target_locale = comptime if (builtin.target.os.tag == .freebsd)
std.mem.trimRight(u8, locale, ".UTF-8")
else
locale;
```
If i force it to always trim the encoding it works, but I am guessing
its there for a reason ,so maybe some of the maintainer can shed some
light in the best way forward, as I am not an expert in how other
systems deal with it. Here you see `Open in Ghostty` -> Abrir con
Ghostty
<img width="353" height="372" alt="image"
src="https://github.com/user-attachments/assets/2c0266f7-cfb3-49e3-aef1-9e98acb16ad8"
/>
- I wanted to format the `py` file with `ruff` but didnt want to drown
the changes, so maybe something that could be worth doing so that also
our `py` files have std formatting.
> [!NOTE]
> Used AI only for helping me debug where the locales could be and why
was it not detected, but no code help whatsoever
This changes the way Ghostty assigns itself and subprocesses to
cgroups and how resource controls are applied.
* Ghostty itself no longer modifies it's own cgroup or moves itself
to a transient scope. To modify the main Ghostty process' resource
controls ensure that you're launching Ghostty with a systemd unit and
use the standard systemd methods for overriding and applying changes
to systemd units.
* If configured (on by default), the process used to run your command
will be moved to a transient systemd scope after it is forked from
Ghostty but before the user's command is executed. Resource controls
will be applied to the transient scope at this time. Changes to
the `linux-cgroup*` configuration entries will not alter existing
commands. If changes are made to the `linux-cgroup*` configuration
entries commands will need to be relaunched. Resource limits can also
be modified after launch outside of Ghostty using systemd tooling. The
transient scope name can be shown by running `systemctl --user whoami`
in a shell running inside Ghostty.
Fixes#2084.
Related to #6669
Example of `systemctl status` showing main Ghostty process and one
surface:
<img width="1132" height="135" alt="Screenshot From 2026-02-07 16-31-14"
src="https://github.com/user-attachments/assets/81dffd0b-8801-4695-adf4-213647cdf0c3"
/>
This fixes the issue where our palette generation was changing our
default palette. The default palette is based on some well known values
chosen from various terminals and it was a bit jarring to have it
change.
We now only auto-generate the palette if the user has customized at
least one entry.