Commit Graph

16910 Commits

Author SHA1 Message Date
Leah Amelia Chen
9e30f70f23 gtk: do not set bell ringing if already focused (#13597) 2026-08-05 14:18:45 +08:00
ghostty-vouch[bot]
2346c4fe47 Update VOUCHED list (#13617)
Triggered by
[comment](https://github.com/ghostty-org/ghostty/issues/12984#issuecomment-5187316604)
from @mitchellh.

Denounce: @jamesarch

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-08-05 03:50:37 +00:00
Mitchell Hashimoto
d7f7a4e736 macOS: rename UpdateState.isIdle to isHidden (#13613) 2026-08-04 19:28:05 -07:00
Mitchell Hashimoto
d2f08f1589 config: limit command translations to GTK (#13615)
Fixes #13614

Only translate the shared default commands when building the GTK
runtime. macOS now use the source strings until we do broader
localization.
2026-08-04 19:27:52 -07:00
Mitchell Hashimoto
a00d155e9c config: limit command translations to GTK
Fixes #13614

Only translate the shared default commands when building the GTK runtime. 
macOS now use the source strings until we do broader localization.
2026-08-04 15:23:09 -07:00
Mitchell Hashimoto
ccb08f35f6 macOS: show cancel update option when its actually cancellable (#13612)
`extracting` and `installing` state aren't cancellable by us.

> Recommend reviewing with whitespace hidden
2026-08-04 14:07:38 -07:00
Lukas
a86c49d7af macOS: rename UpdateState.isIdle to isHidden 2026-08-04 22:38:35 +02:00
Lukas
63d08c0342 macOS: show cancel update option when its actually cancellable
`extracting` and `installing` state aren't cancellable by us
2026-08-04 22:18:35 +02:00
Mitchell Hashimoto
b8ab2ff168 macOS: show/search description when subtitle missing in CommandPalette (#13610)
<img width="1125" height="552" alt="image"
src="https://github.com/user-attachments/assets/09866c9b-d5c4-422f-860b-de4de4cca055"
/>
2026-08-04 11:20:01 -07:00
Mitchell Hashimoto
760a250029 config: formatted action should be parsable into the original (#13609)
This fixes the issue where an action with string as it's parameter is
not working correctly in CommandPalette, found in #9671. For example:

```
command-palette-entry = title:"Set Ghostty Title",description:test sending text.,action:set_tab_title:👻
keybind=cmd+r=set_tab_title:👻
```

Keybind works perfectly, but the title is escaped when triggering in
CommandPalette.

> Introduced in
[#8873](https://github.com/ghostty-org/ghostty/pull/8873/changes#diff-9e7936787320bcf70e332c868125039d8c0a7f96c4a88f2af0af21d952c6830dR1216),
I tested the fixed issue as well, the following config still parses
correctly, mentioned in
https://github.com/ghostty-org/ghostty/issues/8849#issuecomment-3322018212.

```
command-palette-entry = title:Focus Split: Next,description:"Focus the next split, if any.",action:goto_split:next
```

Also `ghostty +show-config` now will also output the readable strings as
well.
<img width="1078" height="428" alt="image"
src="https://github.com/user-attachments/assets/f9dc1447-7b4e-44f4-8362-b54f4d805c7a"
/>
2026-08-04 11:19:37 -07:00
Jeffrey C. Ollie
51cf099678 datastruct: remove unused LRU implementation (#13607) 2026-08-04 13:18:34 -05:00
Lukas
066a0b7c45 macOS: show description when subtitle missing in CommandPalette 2026-08-04 20:03:04 +02:00
Lukas
b67f8ef51d config: don't escape Binding.Action.String 2026-08-04 19:49:37 +02:00
Lukas
8cfbaf545a config: formatted action should be parsable into the original 2026-08-04 19:49:37 +02:00
Uzair Aftab
02f34835ea datastruct: remove unused LRU implementation 2026-08-04 19:46:26 +02:00
Mitchell Hashimoto
48d85eaeb0 core: fix mouse reporting mutex lock 2026-08-04 09:10:14 -07:00
Mitchell Hashimoto
ca56412bf2 gtk: forward middle click to TUIs with mouse reporting (#13108)
Fix for Issue #12940 
I actually do not know if this has already been resolved and the issue
is just still open. Either way, here's a fix. Now we run a check to see
if the current program is accepting mouse events before discarding the
middle click.
2026-08-04 08:53:39 -07:00
ghostty-vouch[bot]
08342c9244 Update VOUCHED list (#13603)
Triggered by [discussion
comment](https://github.com/ghostty-org/ghostty/discussions/13602#discussioncomment-17895866)
from @jcollie.

Vouch: @UnsaltedScholar

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-08-04 15:33:09 +00:00
Mitchell Hashimoto
594ee212bc macos: defer transparent titlebar KVO rebinding (#13601)
Fixes #13386, based on
https://github.com/mustafa0x/ghostty/commit/a8c090

Defer transparent-titlebar KVO rebinding to the next main-queue turn.
Track the observed tab group so unchanged bindings are preserved.

Previously, a tab-group callback could invalidate and recreate its own
observation before returning, leaving closed terminal windows registered
with AppKit after the undo timeout. These windows accumulated titlebar
and layer state, increasing memory use and WindowServer CPU with tab
churn.

Validated with an AppDelegate change that sat and created/closed tabs in
a loop, then counted weak controllers/windows/nsapp window.
2026-08-04 07:20:05 -07:00
Mitchell Hashimoto
363e6e6b42 i18n: translation support for command palete (#11641)
Most obvious next step in translating Ghostty is the command palette.
Added support for i18n.N_ (https://docs.gtk.org/glib/i18n.html#macros).
Made a Latvian translation for the command palette to test. Codex did
bulk of the translations but I verified them.
2026-08-04 06:43:38 -07:00
Mitchell Hashimoto
cfa0ca7106 macos: defer transparent titlebar KVO rebinding
Fixes #13386

Defer transparent-titlebar KVO rebinding to the next main-queue turn.
Track the observed tab group so unchanged bindings are preserved.

Previously, a tab-group callback could invalidate and recreate its own
observation before returning, leaving closed terminal windows registered
with AppKit after the undo timeout. These windows accumulated titlebar and
layer state, increasing memory use and WindowServer CPU with tab churn.

Validated with an AppDelegate change that sat and created/closed tabs
in a loop, then counted weak controllers/windows/nsapp window.

Co-authored-by: Mustafa J <mustafa.0x@gmail.com>
2026-08-04 06:40:41 -07:00
Jon Parise
1f6e26642e config: clarify cursor-click-to-move's relation to shell-integration (#13589) 2026-08-04 08:54:06 -04:00
Lauri Tirkkonen
85083d23cd config: clarify cursor-click-to-move's relation to shell-integration
the original wording is a bit confusing; I thought cursor-click-to-move
required shell-integration to be enabled, and was confused when the
mouse was still moving my cursor in fish even with
shell-integration=none.
2026-08-04 20:59:52 +09:00
ghostty-vouch[bot]
bdd849fc2f Update VOUCHED list (#13596)
Triggered by
[comment](https://github.com/ghostty-org/ghostty/issues/13589#issuecomment-5178660481)
from @jparise.

Vouch: @lotheac

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-08-04 11:57:34 +00:00
Lauri Tirkkonen
f5419b9b15 gtk: do not set bell ringing if already focused 2026-08-04 20:40:32 +09:00
Ēriks Remess
e0744dde62 i18n - command palette - empty translations 2026-08-04 11:05:17 +03:00
Ēriks Remess
1125fa26df i18n: note about i18n.N_ usage and @inComptime() return msgid for i18n._ 2026-08-04 11:04:29 +03:00
Ēriks Remess
df23bef0e9 i18n: translation support for command palete and Latvian translation for it 2026-08-04 11:04:29 +03:00
Mitchell Hashimoto
6687d6089d macos: avoid leaking ports while awaiting accessibility (#13590)
#11799

Creating a CGEventTap without Accessibility permission leaks a Mach port
inside CoreGraphics on every failed attempt. The global keybind listener
retried this once per second while waiting for permission, so Ghostty
eventually exhausted the process port limit.

Request Accessibility access once, poll AXIsProcessTrusted while access
is denied, and create the event tap only after access is granted. Stop
polling before creation so an unrelated tap failure cannot restart the
leaking retry loop.

Tested this with various settings and global keys working fine.
2026-08-03 21:00:05 -07:00
Mitchell Hashimoto
b9d88292be terminal: speed up formatting anywhere from ~1.5x to ~8x (#13587)
This PR speeds up our formatting (plain text, html, and VT) by anywhere
from ~1.5x to ~8x.

The formatter is the hot path behind multiple features in Ghostty GUI:
clipboard copy (plain/VT/HTML), `write_screen_file`, `selectionString`,
and terminal search sliding window. It's also the hot path for
libghostty users, namely people like
[zmx](https://github.com/neurosnap/zmx) which utilize the VT formatter
to restore a terminal.

This PR also adds the benchmarking infrastructure for the formatter.

## How

- **Fast cell-run optimization.** For simple cells (single codepoint, no
style/hyperlink) we encode them as a single run rather than one at a
time.
- **Make some arguments comptime.** Generates more code but benchmarks
show it improves things, specifically for per-format switches that we do
a LOT.
- **Interned style id fast path.** Styles are interned per page, so id
equality implies style equality. We track the id of the active style and
skip the per-cell `Style` copy + `eql` when it matches.
- **Fast printing.** Avoid `std.fmt` where possible and assemble
integers, RGB colors, codepoints in fixed-width buffers with a single
memcpy. This was extracted partially to `fastprint.zig` so we can reuse
it.
- **Avoid double-formatting for tracked pins.** Previously we formatted
twice (once through a `Discarding` writer to count bytes) for pin maps.
Now I'm smarter about it and do a single pass.

## Performance

All on my machine, 80x24 terminal, 10K lines of scrollback.

| workload              | main     | this PR  | speedup | throughput |
| --------------------- | -------- | -------- | ------- | ---------- |
| plain / plain         | 5.74 ms  | 1.67 ms  | 3.4x    | 364 MB/s   |
| plain / vt            | 6.46 ms  | 1.04 ms  | 6.2x    | 596 MB/s   |
| plain / html          | 7.39 ms  | 2.32 ms  | 3.2x    | 308 MB/s   |
| unicode / plain       | 9.74 ms  | 5.42 ms  | 1.8x    | 276 MB/s   |
| unicode / vt          | 10.42 ms | 5.53 ms  | 1.9x    | 275 MB/s   |
| unicode / html        | 12.53 ms | 7.35 ms  | 1.7x    | 509 MB/s   |
| styled / plain        | 5.65 ms  | 1.69 ms  | 3.4x    | 360 MB/s   |
| styled / vt           | 9.07 ms  | 4.20 ms  | 2.2x    | 409 MB/s   |
| styled / html         | 10.78 ms | 6.64 ms  | 1.6x    | 740 MB/s   |
| mixed / plain         | 8.59 ms  | 4.81 ms  | 1.8x    | 226 MB/s   |
| mixed / vt            | 11.25 ms | 6.65 ms  | 1.7x    | 250 MB/s   |
| mixed / html          | 14.47 ms | 10.52 ms | 1.4x    | 414 MB/s   |
| wrapped / plain       | 7.30 ms  | 1.11 ms  | 6.6x    | 733 MB/s   |
| wrapped / vt          | 8.14 ms  | 1.04 ms  | 7.8x    | 789 MB/s   |
| wrapped / html        | 9.00 ms  | 2.12 ms  | 4.2x    | 465 MB/s   |
| pin-map / plain       | 12.51 ms | 3.80 ms  | 3.3x    |            |
| pin-map / vt          | 13.12 ms | 2.99 ms  | 4.4x    |            |
| active screen / plain | 12.5 µs  | 2.7 µs   | 4.6x    |            |
| active screen / vt    | 18.4 µs  | 6.8 µs   | 2.7x    |            |

Workloads: 
- `plain` is ASCII lines
- `unicode` is 2/3/4-byte codepoints with 10% grapheme clusters
- `styled` is heavy SGR churn
- `mixed` is styles + Unicode + hyperlinks
-  `wrapped` is a continuous soft-wrapped stream
- `pin-map`/`active screen` are the selectionString/search-style and
visible-screen-only cases respectively.
2026-08-03 20:56:46 -07:00
Mitchell Hashimoto
e69dc2bee8 renderer: reset terminal state cleanup counter (#13585)
Reset the frame counter whenever retained render state is cleared.
Otherwise, every subsequent frame will be deinitialized and rebuilt.
2026-08-03 20:55:47 -07:00
Mitchell Hashimoto
74b426458b gtk: use native blur on GTK 4.23.3+ (#13586)
Finally, what was previously thought impossible, is now possible.
The blur region itself is far more accurate than what we can conjure up
on our own, and in a much more finetuned and detailed way too.
Thank you, GTK devs!

Closes #13581
2026-08-03 20:55:14 -07:00
Mitchell Hashimoto
2ed67cadd1 terminal: redesign pin map for formatter 2026-08-03 20:50:33 -07:00
Mitchell Hashimoto
2b6a1e41fc macos: avoid leaking ports while awaiting accessibility
#11799

Creating a CGEventTap without Accessibility permission leaks a Mach
port inside CoreGraphics on every failed attempt. The global keybind
listener retried this once per second while waiting for permission, so
Ghostty eventually exhausted the process port limit.

Request Accessibility access once, poll AXIsProcessTrusted while
access is denied, and create the event tap only after access is
granted. Stop polling before creation so an unrelated tap failure
cannot restart the leaking retry loop.
2026-08-03 20:41:39 -07:00
Mitchell Hashimoto
d4391ff835 fastprint: fix compile errors 2026-08-03 20:10:19 -07:00
Mitchell Hashimoto
79aa256fa2 terminal: speed up formatter mostly by avoiding std.fmt 2026-08-03 19:59:28 -07:00
Mitchell Hashimoto
8838c37f4c terminal: fast print styles 2026-08-03 19:55:54 -07:00
Mitchell Hashimoto
85b1dd0dd9 benchmark: formatter benchmark 2026-08-03 19:50:35 -07:00
Mitchell Hashimoto
b11d60818a synthetic: styled output generator 2026-08-03 19:50:20 -07:00
Leah Amelia Chen
3263fc6c4b gtk: use native blur on GTK 4.23.3+
Finally, what was previously thought impossible, is now possible.
The blur region itself is far more accurate than what we can conjure up
on our own, and in a much more finetuned and detailed way too.
Thank you, GTK devs!
2026-08-04 07:16:18 +08:00
Leah Amelia Chen
04f1bc0960 winproto/wayland: disable custom blur on GTK >=4.23.3
GTK 4.23.3 added its own (much smarter) implementation of background blur,
which means our implementation is not only redundant, it also crashes the
program because a surface cannot have multiple associated blur objects.
Ergo, don't do custom blur on newer GTK versions.

See #13578
2026-08-04 07:10:01 +08:00
Jon Parise
9e6e2ea964 renderer: reset terminal state cleanup counter
Reset the frame counter whenever retained render state is cleared.
Otherwise, every subsequent frame will be deinitialized and rebuilt.
2026-08-03 19:07:42 -04:00
Mitchell Hashimoto
f124c42ab9 font/shaper: eliminate grapheme candidate allocations (#13584)
RunIterator allocated a list of font candidates for every
multi-codepoint grapheme, then scanned it for the first font covering
the entire cluster.

Instead, check the primary and additional font candidates as they're
discovered. This preserves their order while removing the temporary
array and avoids additional lookups when the primary font supports the
full grapheme.
2026-08-03 15:56:34 -07:00
Jon Parise
ca8868a295 font/shaper: eliminate grapheme candidate allocations
RunIterator allocated a list of font candidates for every multi-codepoint
grapheme, then scanned it for the first font covering the entire cluster.

Instead, check the primary and additional font candidates as they're
discovered. This preserves their order while removing the temporary
array and avoids additional lookups when the primary font supports the
full grapheme.
2026-08-03 18:22:23 -04:00
Mitchell Hashimoto
7e50356642 terminal: support pending image payloads for kitty graphics (#13582)
Represent Kitty image data as a complete/pending tagged union. Kitty
images can now be completed _later_ if we have all their other metadata
up front.

This will be used by the snapshot API to transmit lightweight
information up front so that renderers of the snapshot can show
placeholders and accept mutating pty data, while the real image data
streams in later.

No user-visible behavior changes today.
2026-08-03 13:28:24 -07:00
Mitchell Hashimoto
5700414f14 libghostty-vt: add C API for snapshotting functions (#13580)
Expose terminal snapshot through the libghostty-vt C API and add a new C
example that runs in CI to verify this stuff works!

## Example

```c
// Enable PTY continuation tracking
size_t continuation_limit = 1024;
assert(ghostty_terminal_set(
    terminal,
    GHOSTTY_TERMINAL_OPT_CONTINUATION_MAX_BYTES,
    &continuation_limit) == GHOSTTY_SUCCESS);

// Encode a terminal with heap allocation
uint8_t *bytes = NULL;
size_t len = 0;
assert(ghostty_snapshot_encode_alloc(
    terminal, NULL, &bytes, &len) == GHOSTTY_SUCCESS);

// Full blocking decode from an owned buffer. 
GhosttySnapshotDecoder decoder = NULL;
assert(ghostty_snapshot_decoder_new_buf(
    NULL, &decoder, bytes, len) == GHOSTTY_SUCCESS);

GhosttyTerminal restored = NULL;
assert(ghostty_snapshot_decoder_decode(
    decoder, &restored) == GHOSTTY_SUCCESS);

ghostty_snapshot_decoder_free(decoder);
ghostty_free(NULL, bytes, len);
```

Streaming decode:

```c
// Streaming decoder from a custom reader IO function.
GhosttyReader reader = {
    .read = read_snapshot,
    .userdata = source,
};
GhosttySnapshotDecoder decoder = NULL;
assert(ghostty_snapshot_decoder_new(
    NULL, &decoder, reader) == GHOSTTY_SUCCESS);

// Read up to the ready state (when we can render and start processing pty bytes)
GhosttyTerminal terminal = NULL;
assert(ghostty_snapshot_decoder_ready(
    decoder, &terminal) == GHOSTTY_SUCCESS);

// Sometime later or async process remaining frames.
GhosttyResult result;
while ((result = ghostty_snapshot_decoder_next(decoder)) ==
       GHOSTTY_SUCCESS) {
  size_t rows = 0;
  assert(ghostty_snapshot_decoder_get(
      decoder,
      GHOSTTY_SNAPSHOT_DECODER_DATA_PROGRESS_ROWS,
      &rows) == GHOSTTY_SUCCESS);
  render(terminal);
}
assert(result == GHOSTTY_NO_VALUE);
```
2026-08-03 13:28:13 -07:00
Mitchell Hashimoto
a011043784 termio: free resources for discarded messages (#13579)
Messages can own allocated data or a derived config. Some paths (writer
thread draining, mailbox shutdown with unread messages, and queue push
failures) discarded messages without releasing those resources.

This change adds Message.deinit and uses it whenever a message is
discarded.
2026-08-03 13:18:27 -07:00
Mitchell Hashimoto
6760c6482b terminal: support pending image payloads for kitty graphics
Represent Kitty image data as a complete/pending tagged union.
Kitty images can now be completed _later_ if we have all their other
metadata up front.

This will be used by the snapshot API to transmit lightweight
information up front so that renderers of the snapshot can show
placeholders and accept mutating pty data, while the real image data
streams in later.
2026-08-03 13:11:28 -07:00
Mitchell Hashimoto
d7bb4b8639 libghostty-vt: add C API for snapshotting functions
Expose terminal snapshot through the libghostty-vt C API and add
a new C example that runs in CI to verify this stuff works!

## Example

```c
size_t continuation_limit = 1024;
assert(ghostty_terminal_set(
    terminal,
    GHOSTTY_TERMINAL_OPT_CONTINUATION_MAX_BYTES,
    &continuation_limit) == GHOSTTY_SUCCESS);

uint8_t *bytes = NULL;
size_t len = 0;
assert(ghostty_snapshot_encode_alloc(
    terminal, NULL, &bytes, &len) == GHOSTTY_SUCCESS);

GhosttySnapshotDecoder decoder = NULL;
assert(ghostty_snapshot_decoder_new_buf(
    NULL, &decoder, bytes, len) == GHOSTTY_SUCCESS);

GhosttyTerminal restored = NULL;
assert(ghostty_snapshot_decoder_decode(
    decoder, &restored) == GHOSTTY_SUCCESS);

ghostty_snapshot_decoder_free(decoder);
ghostty_free(NULL, bytes, len);
```

Streaming decode:

```c
GhosttyReader reader = {
    .read = read_snapshot,
    .userdata = source,
};
GhosttySnapshotDecoder decoder = NULL;
assert(ghostty_snapshot_decoder_new(
    NULL, &decoder, reader) == GHOSTTY_SUCCESS);

GhosttyTerminal terminal = NULL;
assert(ghostty_snapshot_decoder_ready(
    decoder, &terminal) == GHOSTTY_SUCCESS);

GhosttyResult result;
while ((result = ghostty_snapshot_decoder_next(decoder)) ==
       GHOSTTY_SUCCESS) {
  size_t rows = 0;
  assert(ghostty_snapshot_decoder_get(
      decoder,
      GHOSTTY_SNAPSHOT_DECODER_DATA_PROGRESS_ROWS,
      &rows) == GHOSTTY_SUCCESS);
  render(terminal);
}
assert(result == GHOSTTY_NO_VALUE);
```
2026-08-03 13:09:04 -07:00
Jon Parise
2f7fbadb0b termio: free resources for discarded messages
Messages can own allocated data or a derived config. Some paths (writer
thread draining, mailbox shutdown with unread messages, and queue push
failures) discarded messages without releasing those resources.

This change adds Message.deinit and uses it whenever a message is
discarded.
2026-08-03 15:28:01 -04:00