Commit Graph

9000 Commits

Author SHA1 Message Date
Jon Parise
4b9324f48a bash: suppress __ghostty_hook errors in inherited PROMPT_COMMAND
When some tools spawn subshells, PROMPT_COMMAND may be inherited as an
environment variable while the __ghostty_hook function is not (bash
doesn't export functions by default). This causes "command not found"
errors on every prompt in the subshell.

Add 2>/dev/null to the __ghostty_hook entry in PROMPT_COMMAND so that it
silently no-ops in subshells where the function isn't defined. This also
silences any errors from inside __ghostty_hook itself, but those are all
terminal escape sequences and non-actionable.

See: #11245
2026-03-20 09:41:34 -04:00
Mitchell Hashimoto
7966740b48 bash: move __ghostty_preexec_hook into __ghostty_hook (#11674)
We previously used a readonly variable (__ghostty_ps0) to define the
best __ghostty_preexec_hook expansion for the current bash version.

This worked pretty well, but it had the downside of managing another
variable (#11258).

We can instead simplify this a bit by moving this into __ghostty_hook. I
didn't take that approach originally because I wanted to avoid the bash
version check on each command, but slightly loosening our guard check to
just look for "__ghostty_preexec_hook" (rather than the full expansion
expression) means we can bury the bash version check to the cold path.

One small gap here is that we may not update PS0 to the correct syntax
if we start switching between significantly different bash versions in
interactive subshells, but that seems like a pretty rare case to handle
given the benefits of this approach.
2026-03-19 20:13:46 -07:00
Mitchell Hashimoto
549824842d vt: add style and grapheme accessors
Add ghostty_grid_ref_style and ghostty_grid_ref_graphemes to the grid
ref C API, allowing callers to extract the full style and grapheme
cluster directly from a grid reference without manually resolving
the page internals.
2026-03-19 19:48:16 -07:00
Mitchell Hashimoto
df8813bf1b vt: replace ghostty_terminal_cell with GhosttyGridRef API 2026-03-19 19:40:53 -07:00
Jon Parise
2a952b4dfe bash: move __ghostty_preexec_hook into __ghostty_hook
We previously used a readonly variable (__ghostty_ps0) to define the
best __ghostty_preexec_hook expansion for the current bash version.

This works pretty well, but it had the downside of managing another
variable (#11258).

We can instead simplify this a bit by moving this into __ghostty_hook. I
didn't take that approach originally because I wanted to avoid the bash
version check on each command, but slightly loosening our guard check to
just look for "__ghostty_preexec_hook" (rather than the full expansion
expression) means we can bury the bash version check to the cold path.

One small gap here is that we may not update PS0 to the correct syntax
if we start switching between significantly different bash versions in
interactive subshells, but that seems like a pretty rare case to handle
given the benefits of this approach.
2026-03-19 20:59:16 -04:00
Mitchell Hashimoto
0400de28b4 vt: add ghostty_terminal_cell for point-based cell lookup
Add a new C API function ghostty_terminal_cell that retrieves the
opaque cell and row values at a given point in the terminal grid.
The point is a tagged union supporting active, viewport, screen, and
history coordinate systems.
2026-03-19 15:16:55 -07:00
Mitchell Hashimoto
057f227145 terminal: convert Point to lib.Enum/lib.TaggedUnion with C header 2026-03-19 14:01:11 -07:00
Mitchell Hashimoto
5c8b9f3f43 vt: add GhosttyCell and GhosttyRow C API with data getters
Add opaque GhosttyCell (uint64_t) and GhosttyRow (uint64_t) types that
bitcast to the internal packed Cell and Row structs from page.zig. Each
type has a corresponding data enum and getter function following the
same pattern as ghostty_terminal_get.

ghostty_cell_get supports extracting codepoint, content tag, wide
property, has_text, has_styling, style_id, has_hyperlink, protected,
and semantic_content. ghostty_row_get supports wrap, wrap_continuation,
grapheme, styled, hyperlink, semantic_prompt, kitty_virtual_placeholder,
and dirty.

The cell and row types and functions live in a new screen.h header,
separate from terminal.h, with terminal.h including screen.h for
convenience.
2026-03-19 13:15:45 -07:00
Mitchell Hashimoto
d827225573 vt: expand padding for color union to 64-bit to allow for a pointer 2026-03-19 12:24:34 -07:00
Mitchell Hashimoto
d62f6df1d5 vt: expose cursor_style via terminal_get
Add cursor_style to TerminalData, returning the current SGR style
of the cursor (the style applied to newly printed characters) as a
GhosttyStyle.

Refactor the C style conversion helpers: replace the standalone
convertStyle and convertColor functions with fromStyle and fromColor
initializers on the Style and Color extern structs respectively.
2026-03-19 12:08:31 -07:00
Mitchell Hashimoto
7f36e8bd43 vt: add style C API
Expose the terminal Style struct to the C API as GhosttyStyle, a
sized struct with foreground, background, and underline colors
(as tagged unions) plus boolean text decoration flags.

Add ghostty_style_default() to obtain the default style and
ghostty_style_is_default() to check whether a style has all
default values. Wire both through c/style.zig, main.zig, and
lib_vt.zig with the corresponding header in vt/style.h.
2026-03-19 12:02:03 -07:00
Mitchell Hashimoto
f168b3c098 vt: add ghostty_terminal_get for reading terminal state
Add a typed data query API to the terminal C interface, following
the same OutType pattern used by the OSC command data API. The new
ghostty_terminal_get function takes a GhosttyTerminalData tag and
an output pointer, returning GhosttyResult.

Currently exposes cols, rows, cursor x/y position, and cursor
pending wrap state. The GhosttyTerminalData enum is placed with the
other types in the header (before functions) per the ordering
convention.
2026-03-19 11:47:55 -07:00
Jon Parise
b1ad24e24f bash: emit 133;P (instead of 133;A) under ble.sh
ble.sh performs its own cursor positioning so we get multiple newlines
with 133;A's fresh-line behavior. ble.sh is a large enough project to
justify this additional, unambiguous conditional.

See: akinomyoga/ble.sh#684
See: wezterm/wezterm#5072
2026-03-19 11:26:52 -04:00
Jon Parise
1f3a3b41f7 bash: handle PROMPT_COMMAND ending in a newline
We need to handle on more case: when an existing PROMPT_COMMAND ends in
a newline, we don't want to append a ; because that already counts as a
command separator.

We now handle all of these PROMPT_COMMAND cases:

- Ends with ; — no ; added
- Ends with \n or other whitespace — no ; added
- Ends with a command name — ; added as separator

See: #11245
2026-03-18 09:55:34 -04:00
Mitchell Hashimoto
7bf89740dd vt: expose size_report encoding in the C API
Add ghostty_size_report_encode() to libghostty-vt, following the
same pattern as focus encoding: a single stateless function that
writes a terminal size report escape sequence into a caller-provided
buffer.

The size_report.zig Style enum and Size struct now use lib.Enum and
lib.Struct so the types are automatically C-compatible when building
with c_abi, eliminating the need for duplicate type definitions in
the C wrapper. The C wrapper in c/size_report.zig re-exports these
types directly and provides the callconv(.c) encode entry point.

Supports mode 2048 in-band reports and XTWINOPS responses (CSI 14 t,
CSI 16 t, CSI 18 t).
2026-03-17 16:33:57 -07:00
Mitchell Hashimoto
a1d7ad9243 terminal: extract size report encoder
Size report escape sequences were previously formatted inline in
Termio.sizeReportLocked, and termio.Message carried a duplicate enum for
report styles. That made the encoding logic harder to reuse and kept
the style type scoped to termio.

Move the encoding into terminal.size_report and export it through
terminal.main. The encoder now takes renderer.Size directly and derives
grid and pixel dimensions from one source of truth. termio.Message now
aliases terminal.size_report.Style, and Termio writes reports via the
shared encoder.
2026-03-17 16:21:34 -07:00
Mitchell Hashimoto
d9070dbee2 Fix tmux control parser premature %end/%error block termination (#11597)
Fixes [#11935.](https://github.com/ghostty-org/ghostty/issues/11395)

I’m new to Zig, so I used AI assistance (Codex) while preparing this
change. Before opening this PR, I manually reviewed every line of the
final patch and stepped through the parser in LLDB to verify the
behavior. Happy to make any changes.

To better understand the parser, I also built a small model-checker
model
[here](https://gist.github.com/wyounas/284036272ba5893b6e413cafe2fe2a24).

Separately from this fix, I think formal verification and modeling could
be useful for parser work in Ghostty. The model is written in FizzBee,
which uses a Python-like Starlark syntax and is fairly readable. If that
seems useful, I’d be happy to open a separate discussion about whether
something like that belongs in the repository as executable
documentation or an additional safety net for future parser changes.
2026-03-17 10:32:14 -07:00
Mitchell Hashimoto
4a88f460c4 terminal/tmux: stylistic cleanups 2026-03-17 08:58:22 -07:00
Mitchell Hashimoto
3a65bd5c4f zsh: use OSC 133;P;k=s for secondary prompts (#11596)
This is consistent with our bash prompt handling and also lets us
simplify our multiline prompt logic (because it no longer needs to work
around 133;A's fresh-line behavior).
2026-03-17 08:35:07 -07:00
Jon Parise
3e0d434e8a zsh: use OSC 133;P;k=s for secondary prompts
This is consistent with our bash prompt handling and also lets us
simplify our multiline prompt logic (because it no longer needs to work
around 133;A's fresh-line behavior).
2026-03-17 10:37:40 -04:00
Jeffrey C. Ollie
79162279d9 gtk: move audio playback into separate file, enabling reuse 2026-03-16 19:53:24 -05:00
Jeffrey C. Ollie
f6e92b6895 gtk: refactor application id and resource path (#11580) 2026-03-16 18:43:35 -05:00
Jeffrey C. Ollie
8a40e37b86 gtk: refactor application id and resource path 2026-03-16 18:26:37 -05:00
Mitchell Hashimoto
a460743b2a vt: add mode report encoding to C API
Add ghostty_mode_report_encode() which encodes a DECRPM response
sequence into a caller-provided buffer. The function takes a mode
tag, a report state integer, an output buffer, and writes the
appropriate CSI sequence (with ? prefix for DEC private modes).

The Zig-side ReportState is a non-exhaustive c_int enum that uses
std.meta.intToEnum for safe conversion to the internal type,
returning GHOSTTY_INVALID_VALUE on overflow. The C header exposes
a GhosttyModeReportState enum with named constants for the five
standard DECRPM state values.
2026-03-16 16:10:25 -07:00
Mitchell Hashimoto
1c03770e2b vt: expose terminal modes to C API
Add modes.h with GhosttyModeTag (uint16_t) matching the Zig ModeTag
packed struct layout, along with inline helpers for constructing and
inspecting mode tags. Provide GHOSTTY_MODE_* macros for all 39
built-in modes (4 ANSI, 35 DEC), parenthesized for safety.

Add ghostty_terminal_mode_get and ghostty_terminal_mode_set to
terminal.h, both returning GhosttyResult so that null terminals
and unknown mode tags return GHOSTTY_INVALID_VALUE. The get function
writes its result through a bool out-parameter.

Add a note in the Zig mode entries reminding developers to update
modes.h when adding new modes.
2026-03-16 16:01:53 -07:00
Mitchell Hashimoto
d6b37ba38f terminal: extract DECRPM mode report encoding to terminal package
This extracts our mode reporting from being hardcoded in termio
to being reusable in the existing `terminal.modes` namespace. The goal
is to expose this via the Zig API libghostty (done) and C API (to do
later).
2026-03-16 15:09:26 -07:00
Mitchell Hashimoto
e90dbc9da6 vt: expose focus encoding in C and Zig APIs (#11577)
Add focus event encoding (CSI I / CSI O) to the libghostty-vt public
API, following the same patterns as key and mouse encoding.

The focus Event enum uses lib.Enum for C ABI compatibility. The C API
provides ghostty_focus_encode() which writes into a caller-provided
buffer and returns GHOSTTY_OUT_OF_SPACE with the required size when the
buffer is too small.

Also update key and mouse encoders to return GHOSTTY_OUT_OF_SPACE
instead of GHOSTTY_OUT_OF_MEMORY for buffer-too-small errors, reserving
OUT_OF_MEMORY for actual allocation failures. Update all corresponding
header documentation.
2026-03-16 15:02:03 -07:00
Mitchell Hashimoto
bed9d92f04 vt: expose focus encoding in C and Zig APIs
Add focus event encoding (CSI I / CSI O) to the libghostty-vt public
API, following the same patterns as key and mouse encoding.

The focus Event enum uses lib.Enum for C ABI compatibility. The C API
provides ghostty_focus_encode() which writes into a caller-provided
buffer and returns GHOSTTY_OUT_OF_SPACE with the required size when
the buffer is too small.

Also update key and mouse encoders to return GHOSTTY_OUT_OF_SPACE
instead of GHOSTTY_OUT_OF_MEMORY for buffer-too-small errors,
reserving OUT_OF_MEMORY for actual allocation failures. Update all
corresponding header documentation.
2026-03-16 14:38:19 -07:00
Leah Amelia Chen
2318e18df3 gtk/wayland: refactor global handling (#11559) 2026-03-16 18:06:37 +00:00
Leah Amelia Chen
8966d37985 gtk/wayland: refactor global handling
The way we originally handled globals gradually escalated into an unholy
mess of ad-hoc helper functions and special-case handlers, which proved
to be hard to scale. Using a type-erased EnumMap like this makes
everything *far* easier to work and reason with, I think.

Also nuked the `xdg_wm_dialog_v1` hack that was necessary to prevent
old versions of gtk4-layer-shell crashing. If by the time of 1.4's
release people are still using those versions, it's on them.
2026-03-17 01:14:59 +08:00
Mitchell Hashimoto
a811b6074b Lots of duplicate word typos + typo (#11539)
TL;DR: this description is (intentionally) nonsense but I ran
`\b(\w+)\s\1\b` over `src` and stole a singular typo fix from #11528.

Replacement of #11528 with 100% less slop and 99% less AI; I didn't feel
like saying no to free(ish) typo checking. Note that many of the fixes
there were outright incorrect (and clearly had no review from sentient
lifeforms, contrary to its—sorry, it's—description). A lot of extra
double words were caught with a handy `rg --pcre2 '\b(\w+)\s+\1\b' src`;
you could say this PR was “ripgrep-assisted” the way that one was
“AI-assisted”. Rather ironic since that PR also claims to have used grep
via Claude Code, but missed a lot of them.

The its → it's changes from that PR were elided; I decided to run a `rg
"\bit'?s\b" src`, but someone REALLY likes their its, so I reverted my
changes as there were an extremely large number of changes (probably a
hundred files with multiple hundred cases). The only other change was
“baout” → “about”.

# AI Usage

Claude Code was used by proxy for finding baout. Claude Code was used by
proxy for realizing that the correct spelling is about. Claude Code was
not used for fixing it. Oh my god it was so difficult to fix, the
original PR had it so easy. I had to type out the file name (fish's AI
sorry I mean autocomplete helped though) and like, type /baout, press R,
press ab, then save and exit. This is so difficult you know we should
use an AI for this, like this is so hard I don't know how people manage.

All changes were verified by me: I consulted the dictionary to delve
into double-checkment of “in existence; being in evidence; apparent.”
Uhhh insert assorted other AI impersonation here maybe? THE LLM IN ME
WANTS TO ESCAPE PLEASE HELP
2026-03-16 09:52:35 -07:00
Mitchell Hashimoto
cce205d01b cli: add +explain-config (#11546)
This is a new CLI action that prints an option or keybind's help
documentation to stdout.

    ghostty +explain-config font-size
    ghostty +explain-config copy_to_clipboard
    ghostty +explain-config --option=font-size
    ghostty +explain-config --keybind=copy_to_clipboard

The --option and --keybind flags perform a specific lookup. A string
passed as a positional argument attempts to look up the name first as an
option and then as a keybind.

Our vim plugin uses this with &keywordprg, which allows you to look up
the documentation for the config option or keybind under the cursor (K).
2026-03-16 09:51:48 -07:00
Jon Parise
925992abd9 shell-integration: fix ssh-env SetEnv clobbering user SSH config (#11518)
## Problem

Ghostty's `ssh-env` shell integration uses `-o "SetEnv
COLORTERM=truecolor"` when wrapping SSH commands. OpenSSH treats
command-line `-o SetEnv` options as **replacements** for all `SetEnv`
entries in `~/.ssh/config`, not additions. This silently drops any
user-configured `SetEnv` variables.

For example, a user with this in their SSH config:
```
Host myserver
  SetEnv MY_VAR=hello
```
...would find `MY_VAR` empty after SSHing through Ghostty with `ssh-env`
enabled.

Reference: https://github.com/ghostty-org/ghostty/discussions/10871

## Fix

Replace `-o "SetEnv COLORTERM=truecolor"` with the additive pattern: set
`COLORTERM=truecolor` locally before the SSH call and forward it via
`SendEnv`.

`SendEnv` is additive — it does not clobber `SetEnv` entries in
`~/.ssh/config`.

**Trade-off:** `SendEnv` requires `AcceptEnv COLORTERM` on the remote
server (unlike `SetEnv`). But this was already the case for
`TERM_PROGRAM`/`TERM_PROGRAM_VERSION`, so it's a consistent and
acceptable approach.

## Changes

All 5 shell integration files updated with the same pattern:

- `SetEnv COLORTERM=truecolor` option removed
- `COLORTERM` added to the existing `SendEnv` option
- `COLORTERM=truecolor` set as a local env var on the execute line (so
`SendEnv` has something to forward)

## Test plan

- [ ] Enable `ssh-env` in Ghostty config: `shell-integration-features =
ssh-env`
- [ ] Add `SetEnv MY_VAR=hello` under a host in `~/.ssh/config` and
`AcceptEnv MY_VAR` in `/etc/ssh/sshd_config` on the remote
- [ ] SSH to that host — `echo $MY_VAR` should return `hello` (was empty
before this fix)
- [ ] `echo $COLORTERM` returns `truecolor` (requires `AcceptEnv
COLORTERM`)
- [ ] `echo $TERM_PROGRAM` still propagates (same `AcceptEnv`
requirement as before)
2026-03-16 08:01:33 -04:00
Waqas Younas
978abdeebc Fix tmux control block terminator parsing 2026-03-16 12:46:54 +05:00
Leah Amelia Chen
600f59ae31 gtk: implement quick-terminal-screen for Linux/Wayland (#11117) 2026-03-16 06:30:02 +00:00
Mitchell Hashimoto
de87456a9b lib/vt: export mouse encoding API
Export mouse_encode types and functions through the lib_vt public
input API, mirroring the existing key encoding exports. This adds
MouseAction, MouseButton, MouseEncodeOptions, MouseEncodeEvent,
and encodeMouse so that consumers of the Zig module can encode
mouse events without reaching into internal packages.
2026-03-15 20:16:00 -07:00
Mitchell Hashimoto
9b35c2bb65 vt: add mouse encoding C API
Expose the internal mouse encoding functionality through the C API,
following the same pattern as the existing key encoding API. This
allows external consumers of libvt to encode mouse events into
terminal escape sequences (X10, UTF-8, SGR, URxvt, SGR-Pixels).

The API is split into two opaque handle types: GhosttyMouseEvent
for building normalized mouse events (action, button, modifiers,
position) and GhosttyMouseEncoder for converting those events into
escape sequences. The encoder is configured via a setopt interface
supporting tracking mode, output format, renderer geometry, button
state, and optional motion deduplication by last cell.

Encoder state can also be bulk-configured from a terminal handle
via ghostty_mouse_encoder_setopt_from_terminal. Failed encodes due
to insufficient buffer space report the required size without
mutating deduplication state.
2026-03-15 20:09:54 -07:00
Mitchell Hashimoto
79e023b65e terminal,renderer: convert structs to extern
Convert Coordinate in terminal/point.zig and CellSize, ScreenSize,
GridSize, and Padding in renderer/size.zig to extern structs. All
fields are already extern-compatible types, so this gives them a
guaranteed C ABI layout with no functional change.
2026-03-15 19:39:17 -07:00
Mitchell Hashimoto
37efac99b0 terminal/mouse: convert Event and Format to lib.Enum
Convert the Event and Format enums from fixed-size Zig enums to
lib.Enum so they are C ABI compatible when targeting C. The motion
method on Event becomes a free function eventIsMotion since lib.Enum
types cannot have declarations.
2026-03-15 19:35:54 -07:00
Jon Parise
9783f6c79c cli: tests for +explain-config explain functions 2026-03-15 21:04:35 -04:00
Jon Parise
cef1f19d24 cli: add +explain-config
This is a new CLI action that prints an option or keybind's help
documentation to stdout.

    ghostty +explain-config font-size
    ghostty +explain-config copy_to_clipboard
    ghostty +explain-config --option=font-size
    ghostty +explain-config --keybind=copy_to_clipboard

The --option and --keybind flags perform a specific lookup. A string
passed as a positional argument attempts to look up the name first as an
option and then as a keybind.

Our vim plugin uses this with &keywordprg, which allows you to look up
the documentation for the config option or keybind under the cursor (K).
2026-03-15 20:58:43 -04:00
Mitchell Hashimoto
f9f92f2e0f terminal: consolidate mouse types into mouse.zig
Move MouseEvent and MouseFormat out of Terminal.zig and MouseShape out
of mouse_shape.zig into a new mouse.zig file. The types are named
without the Mouse prefix inside the module (Event, Format, Shape) and
re-exported with the prefix from terminal/main.zig for external use.

Update all call sites (mouse_encode.zig, surface_mouse.zig, stream.zig)
to import through terminal/main.zig or directly from mouse.zig. Remove
the now-unused mouse_shape.zig.
2026-03-15 15:48:36 -07:00
Mitchell Hashimoto
ac5e57ce67 input: extract mouse encoding to a pure, testable file
Move mouse event encoding logic from Surface.zig into a new
input/mouse_encode.zig file.

The new file encapsulates event filtering (shouldReport),
button code computation, viewport bounds checking, motion
deduplication, and all five wire formats (X10, UTF-8, SGR,
urxvt, SGR-pixels). This makes the encoding independently
testable and adds unit tests covering each format and edge
case.

Additionally, Surface `mouseReport` can no longer fail, since the only
failure mode is no buffer space which should be impossible. Updated
the signature to remove the error set.
2026-03-15 15:37:54 -07:00
Kat
6fabf775bb Lots of duplicate word typos + typo. 2026-03-16 09:19:09 +11:00
Mitchell Hashimoto
943d3d2e89 vt: add setopt_from_terminal to C API
Expose the key encoder Options.fromTerminal function to the C API as
ghostty_key_encoder_setopt_from_terminal. This lets C callers sync all
terminal-derived encoding options (cursor key application mode, keypad
mode, alt escape prefix, modifyOtherKeys, and Kitty flags) in a single
call instead of setting each option individually.
2026-03-15 07:05:07 -07:00
John Mars
69554f414c shell-integration: fix ssh-env SetEnv clobbering user SSH config 2026-03-15 01:50:32 -04:00
Jaeseok Lee
86d9a04ece config: add equal option to window-padding-balance
Change `window-padding-balance` from `bool` to an enum with three
values:

- `false` - no balancing (default, unchanged)
- `true` - balance with vshift that caps top padding and shifts excess
  to bottom (existing behavior, unchanged)
- `equal` - balance whitespace equally on all four sides

This gives users who prefer truly equal padding a way to opt in without
changing the default behavior.
2026-03-15 13:50:21 +09:00
Mitchell Hashimoto
647f5adf55 terminal/formatter: safely cast discarding.count to usize
The Discarding writer count field is u64, but several call sites
pass it where a usize is expected. On wasm32-freestanding, usize is
32-bit, so this caused compilation errors.

Use std.math.cast instead of a bare @intCast so that overflow is
handled gracefully, returning WriteFailed rather than triggering
safety-checked undefined behavior at runtime.
2026-03-14 15:29:53 -07:00
Mitchell Hashimoto
4ad7d03c56 terminal/formatter: safely cast discarding.count to usize
The Discarding writer count field is u64, but appendNTimes expects
usize which is u32 on 32-bit targets like arm-linux-androideabi.
Use std.math.cast instead of @intCast to safely handle the
conversion, returning WriteFailed on overflow rather than risking
undefined behavior.
2026-03-14 15:22:21 -07:00
Mitchell Hashimoto
3c8feda118 vt: add format_alloc to C API formatter
Rename the existing format function to format_buf to clarify that it
writes into a caller-provided buffer. Add a new format_alloc variant
that allocates the output buffer internally using the provided
allocator (or the default if NULL). The caller receives the allocated
pointer and length and is responsible for freeing it.

This is useful for consumers that do not know the required buffer size
ahead of time and want to avoid the two-pass query-then-format pattern
needed with format_buf.
2026-03-14 14:59:41 -07:00