Commit Graph

17413 Commits

Author SHA1 Message Date
Mitchell Hashimoto
c8634f3fce terminal: reload cell pointers when print grows a page (#13960)
Fixes #11261.

`Terminal.print`'s grapheme path caches a `*Cell` for the previous cell
and keeps using it after writing other cells. Writing the wide spacer
tail calls `printCell`, which can grow the page to make room for the
cursor hyperlink. Growing clones the page and frees the old one, so the
cached pointer dangles and the following `appendGrapheme` writes into
freed memory. The second test case in the issue reproduces it.

Rather than recomputing `prev` on every use, which is too expensive for
this path, the fix records the cursor page identity before the spacer
write and reloads the cell only if the page actually changed. Node
pointer plus serial is used because nodes are pooled and a replacement
can land on the same address. Nothing changes when the page does not
grow.

Three other pointers in the same function were held across an operation
that can replace a page, so they are now read through the cursor or a
freshly resolved pin: the grapheme move after a wrap, the grapheme
append loop, and `printCell`'s assert on a failed hyperlink write.

Tests:

- `Terminal: VS16 widening when the spacer tail grows the page` fills
the page hyperlink map so the spacer tail is what forces growth. It
crashes without the fix.
- `Terminal: grapheme transfer when widening wraps to the next line`
covers the wrap path where the previous cell already holds grapheme
data, which had no test before.

`zig build test` passes.
2026-08-21 12:51:35 -07:00
ghostty-vouch[bot]
74a133ea17 Update VOUCHED list (#13961)
Triggered by
[comment](https://github.com/ghostty-org/ghostty/issues/13960#issuecomment-5374295208)
from @jcollie.

Vouch: @ArneshBanerjee

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-08-21 19:25:42 +00:00
Arnesh
33cda4dc5d terminal: reload cell pointers when print grows a page
Terminal.print's grapheme path holds a raw pointer to the previous cell
while it writes other cells. Writing the wide spacer tail can grow the
page to fit the cursor hyperlink, and growing replaces the page, so the
pointer is left dangling and the following appendGrapheme writes into
freed memory.

Record the cursor page identity (node pointer plus serial, since pooled
nodes can reuse an address) before the spacer write and reload the cell
only when the page actually changed, so the common path costs nothing.

The same function had three more pointers held across an operation that
can replace a page: the grapheme move after a wrap, the grapheme append
loop, and printCell's assert on a failed hyperlink write. Those now read
through the cursor or a freshly resolved pin.

Fixes #11261
2026-08-22 00:19:58 +05:30
Mitchell Hashimoto
a88ad03e69 kitty graphics: animation support (#13943)
Fixes #5255

This adds support for the Kitty graphics animation frames
(https://sw.kovidgoyal.net/kitty/graphics-protocol/#animation),
completely (transmission, control, composition, rendering, etc.).

This does it in a somewhat naive way: we pre-compose all frames and
store the full RGBA in-memory. Animation is already rare enough, and I
wanted to focus on things working first, so I didn't optimize this very
well. I also wanted this PR to be relatively understandable up front. We
can add complexity later.

But, this adds very little overhead to a non-animation using Kitty
graphics user. The animation state is heap-allocated only when its
needed. So, it just costs a pointer sized field on every image. Plus a
little bit of overhead in the loading state (which itself is heap
allocated during image load only).

On the renderer side, this **unifies Kitty graphics animations and
custom shader animations into a single animation abstraction.** This
simplified our renderer thread and made the generic renderer more
complicated (slightly, its not much!).

**AI usage:** Test writing, validation against the spec/reference
implementation. I drove the main architecture and shaped out the
functions and params, animation storage, etc. I had AI fill in some of
the blanks that I spaced out. Commit messages, comments, and this PR
message are written by me.

## Demo



https://github.com/user-attachments/assets/91be3d66-a5ff-4cab-b3e9-e672f39861c9
2026-08-21 10:22:47 -07:00
Mitchell Hashimoto
4be9d782e3 terminal: avoid Kitty image reset test if no kitty images 2026-08-21 10:00:06 -07:00
Mitchell Hashimoto
dff13b41c9 pkg/{afl++,wuffs}: fix builds for CI 2026-08-21 09:45:59 -07:00
Mitchell Hashimoto
322d7ae789 terminal/kitty: switch to wuffs for pixel work 2026-08-21 09:45:58 -07:00
Mitchell Hashimoto
f3e98fb72b terminal/kitty: X handling properly since 0.45 fix 2026-08-21 09:45:58 -07:00
Mitchell Hashimoto
aee7bf3475 renderer: drive kitty graphics animation 2026-08-21 09:45:58 -07:00
Mitchell Hashimoto
73903f76aa terminal/c: image data returns the current animation frame 2026-08-21 09:45:58 -07:00
Mitchell Hashimoto
d2ffeb5ba5 terminal/kitty: execute animation commands 2026-08-21 09:45:58 -07:00
Mitchell Hashimoto
d8b920e504 terminal/kitty: animation frame storage, composition, and playback 2026-08-21 09:45:58 -07:00
Mitchell Hashimoto
4b915e3bc4 terminal/kitty: complete animation command parsing 2026-08-21 09:45:58 -07:00
Mitchell Hashimoto
d6171332cd update mirror, support git+https dependencies (#13957) 2026-08-21 09:45:47 -07:00
Mitchell Hashimoto
ffad4c6ec4 update mirror, support git+https dependencies 2026-08-21 09:36:56 -07:00
trag1c
fa7fe3b3af po/zh_TW: add missing translations (#13690)
Translate the remaining 181 untranslated strings in po/zh_TW.po,
bringing it to 100% coverage (252/252), including the strings added by
the recent template update.

All 72 existing translations are preserved unchanged.
2026-08-21 18:17:26 +02:00
Mitchell Hashimoto
442046f8ee terminal/kitty: preserve image limits across full reset (#13951)
RIS previously cleared configured storage limits and allowed mediums. We
now retain this properly.
2026-08-21 08:12:11 -07:00
Mitchell Hashimoto
bcbc93a6b9 terminal/kitty: preserve image limits across full reset
RIS previously cleared configured storage limits and allowed mediums. 
We now retain this properly.
2026-08-21 08:03:20 -07:00
Mitchell Hashimoto
99d7b5fd50 macOS: clean up GlassViewModel (#13944)
Forgot to delete the autocomplete template in the previous pr...
2026-08-21 06:24:05 -07:00
Mitchell Hashimoto
ac9a2c4cd5 pkg/wuffs: build without libc (#13942)
This modifies our wuffs dependency so that it no longer requires libc.

This unblocks using wuffs from libghostty-vt on freestanding targets,
which we'll eventually want for some Kitty graphics stuff.
2026-08-21 05:51:42 -07:00
ghostty-vouch[bot]
311d383766 Update VOUCHED list (#13948)
Triggered by [discussion
comment](https://github.com/ghostty-org/ghostty/discussions/13947#discussioncomment-18105162)
from @trag1c.

Denounce: @DIDOUOUGHA

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-08-21 11:44:16 +00:00
Lukas
10d7326889 macOS: clean up GlassViewModel 2026-08-21 11:17:54 +02:00
Mitchell Hashimoto
619555d1cc pkg/wuffs: build without libc
This modifies our wuffs dependency so that it no longer requires libc.

This unblocks using wuffs from libghostty-vt on freestanding targets,
which we'll eventually want for some Kitty graphics stuff.
2026-08-20 21:34:45 -07:00
Mitchell Hashimoto
42a161aada pkg/wuffs: fix gray+alpha to RGBA swizzle failing for all inputs (#13941)
The gaToRgba swizzle requested a YA_PREMUL source pixel format from the
wuffs pixel swizzler, but wuffs does not support YA_PREMUL as a swizzle
source. As a result, gaToRgba returned error.WuffsError for every input.

The path can't happen in Ghostty GUI today since our PNG decoding always
produces RGBA, but it is possible via libghostty that submit grey+alpha
directly.
2026-08-20 21:24:23 -07:00
Mitchell Hashimoto
86db30785c pkg/wuffs: fix gray+alpha to RGBA swizzle failing for all inputs
The gaToRgba swizzle requested a YA_PREMUL source pixel format from
the wuffs pixel swizzler, but wuffs does not support YA_PREMUL as a
swizzle source. As a result, gaToRgba returned error.WuffsError for every input.

The path can't happen in Ghostty GUI today since our PNG decoding always
produces RGBA, but it is possible via libghostty that submit grey+alpha
directly.
2026-08-20 20:43:24 -07:00
Mitchell Hashimoto
08450e21e5 kitty graphics: support relative placements (#13939)
This adds full support for relative placements:
https://sw.kovidgoyal.net/kitty/graphics-protocol/#relative-placements
(`R=` and `Q=`). The high-level description is that this allows images
to be placed relative to other images. For more details, the spec
explains it better than I can, or the demo video below.

The implementation here was pretty straightforward. It mainly revolved
around adding parent resolution logic to puts (and validation), and
proper unparented placement reaping in the right points, and then the
various tests around that. The renderer logic itself was also
straigthforward, I had to add some offset resolution to the core
graphics storage but we can reuse that. The main complexity -- as always
-- (MY OWN EMDASHES) is Unicode placeholders. But... also not too hard.

After this, the only feature we don't support from the protocol is
animation.

**AI usage:** Test writing, validation against spec/reference, judging,
and some "fill in the function/block". I setup the overall shape of the
implementation.

## Demo

Ghostty on the left, Kitty on the right



https://github.com/user-attachments/assets/dc2ec3ac-480a-4587-94e4-06a842f9b3f4
2026-08-20 15:46:33 -07:00
Mitchell Hashimoto
9490f71342 renderer: position relative kitty image placements 2026-08-20 15:16:17 -07:00
Mitchell Hashimoto
33ca9e5ddc terminal/kitty: create relative placements from put commands 2026-08-20 15:16:11 -07:00
Mitchell Hashimoto
f5b3efe452 terminal/c: resolve relative placement viewport positions 2026-08-20 15:16:03 -07:00
Mitchell Hashimoto
cd5f9eef0a terminal/kitty: add relative placement storage 2026-08-20 15:15:55 -07:00
Mitchell Hashimoto
a32a100d4b terminal/kitty: centralize placeholder placement lookup 2026-08-20 15:15:54 -07:00
Mitchell Hashimoto
d30a9c424d terminal: clear cursor pin garbage flag on screen reset 2026-08-20 15:15:48 -07:00
Mitchell Hashimoto
16b39f45a8 fix up recent Valgrind errors (#13938)
No issues in actual shipped code found.

- ci: valgrind runs now fail when Memcheck finds an unsuppressed error.
- fix real memory issues in unit tests
- add a suppression for Zig's flate compression which documents as
purposely using undefined memory
2026-08-20 14:17:14 -07:00
Mitchell Hashimoto
04d6954ebf os: remove flaky TempDir handle checks (#13937)
The TempDir tests saved directory descriptors before close and expected
`fcntl(F_GETFD)` to return `EBADF` afterward. A descriptor number
becomes available for reuse as soon as it is closed.

Unrelated I/O could reuse either number before the assertion, causing
the Valgrind job to report a leak even though TempDir closed both
handles correctly [1].

Remove the raw descriptor assertions and keep coverage of observable
delete and retain behavior. Explain at both close sites why descriptor
numbers cannot identify the original handles after close.

[1]:
https://github.com/ghostty-org/ghostty/actions/runs/32410237002/job/96561246487
2026-08-20 14:17:02 -07:00
Mitchell Hashimoto
c5f4f00ed3 fix up recent Valgrind errors
- ci: valgrind runs now fail when Memcheck finds an unsuppressed error.
- fix real memory issues in unit tests
- add a suppression for Zig's flate compression which documents as
  purposely using undefined memory
2026-08-20 13:56:12 -07:00
Mitchell Hashimoto
1ffdd7415a os: remove flaky TempDir handle checks
The TempDir tests saved directory descriptors before close and expected
`fcntl(F_GETFD)` to return `EBADF` afterward. A descriptor number
becomes available for reuse as soon as it is closed.

Unrelated I/O could reuse either number before the assertion, causing
the Valgrind job to report a leak even though TempDir closed both
handles correctly [1].

Remove the raw descriptor assertions and keep coverage of observable
delete and retain behavior. Explain at both close sites why descriptor
numbers cannot identify the original handles after close.

[1]: https://github.com/ghostty-org/ghostty/actions/runs/32410237002/job/96561246487
2026-08-20 13:32:30 -07:00
Mitchell Hashimoto
1ffa77c90b terminal/kitty: scroll and clip image placements within margins (#13935)
Fixes #4323

The Kitty graphics protocol requires that when margins are defined and
index commands are used, only images entirely within the scroll region
are scrolled, and that they are clipped when scrolling would cause them
to extend outside the region [1].

This implements that part of the spec.

Benchmarked pure `\n` terminal streams to ensure that the no-image case
is not regressed. Our branch hints plus checks on placements keep that
true. When images are present, things get a lot slower but thats
acceptable for now.

## Demo



https://github.com/user-attachments/assets/22e23c8b-ffd4-48fb-9e86-31e810d36ea0



[1]:
https://sw.kovidgoyal.net/kitty/graphics-protocol/#interaction-with-other-terminal-actions
2026-08-20 12:43:40 -07:00
Mitchell Hashimoto
90bce0d2dd terminal/kitty: scroll and clip image placements within margins
Fixes #4323

The Kitty graphics protocol requires that when margins are defined
and index commands are used, only images entirely within the scroll
region are scrolled, and that they are clipped when scrolling would
cause them to extend outside the region [1]. 

This implements that part of the spec.

Benchmarked pure `\n` terminal streams to ensure that the no-image case
is not regressed. Our branch hints plus checks on placements keep that
true. When images are present, things get a lot slower but thats
acceptable for now.

[1]: https://sw.kovidgoyal.net/kitty/graphics-protocol/#interaction-with-other-terminal-actions
2026-08-20 12:30:15 -07:00
Mitchell Hashimoto
e6605009bb terminal/kitty: prevent auto-assigned image ID collisions (#13934)
Fixes #2197

Image transmissions without an explicit ID (i=) were assigned IDs from a
wrapping counter starting with no collision check. The protocol allows
clients to choose IDs anywhere in the u32 range, so an auto-assigned ID
could collide.

Number-based transmissions (I= without i=) now receive the smallest ID
not currently in use. This probes the image map in an `O(N)` fashion but
performance issues here require a pathological client and this
implementation matches Kitty's performance as well.
2026-08-20 10:12:14 -07:00
Mitchell Hashimoto
6b23c584ca terminal/kitty: prevent auto-assigned image ID collisions
Fixes #2197

Image transmissions without an explicit ID (i=) were assigned IDs from
a wrapping counter starting with no collision check. The protocol allows
clients to choose IDs anywhere in the u32 range, so an auto-assigned ID could
collide.

Number-based transmissions (I= without i=) now receive the smallest ID
not currently in use. This probes the image map in an `O(N)` fashion but
performance issues here require a pathological client and this implementation
matches Kitty's performance as well.
2026-08-20 09:45:28 -07:00
Mitchell Hashimoto
b6cbaf54ef terminal/kitty: fix various validation behaviors to match Kitty (#13933)
There are various validation behaviors we did that matched the spec but
didn't match Kitty, because Kitty is written in C (these parts) and does
a lot of C-ish things (like bool is any non-zero value, despite the spec
saying 1/0).

This also fixes a more major issue where invalid formats should be
deferred until transmission finishes so we send a proper response. Right
now we send no response which can cause a client to hang!
2026-08-20 09:27:55 -07:00
Mitchell Hashimoto
242723223f terminal/kitty: fix various validation behaviors to match Kitty
There are various validation behaviors we did that matched the spec but
didn't match Kitty, because Kitty is written in C (these parts) and does
a lot of C-ish things (like bool is any non-zero value, despite the spec
saying 1/0). 

This also fixes a more major issue where invalid formats should be
deferred until transmission finishes so we send a proper response. Right
now we send no response which can cause a client to hang!
2026-08-20 09:19:53 -07:00
Mitchell Hashimoto
48c7006b9a terminal/kitty: accept empty graphics delete ranges (#13932)
The `d=r`/`d=R` delete parser required a `y` key and enforced `x <= y`,
rejecting the entire command with `error.InvalidFormat` otherwise. Both
bounds now default to zero and neither is validated.

This matches upstream reference implementation.
2026-08-20 09:09:45 -07:00
Mitchell Hashimoto
f7d29b19e8 terminal/kitty: accept empty graphics delete ranges
The `d=r`/`d=R` delete parser required a `y` key and enforced `x <= y`,
rejecting the entire command with `error.InvalidFormat` otherwise. Both
bounds now default to zero and neither is validated.

This matches upstream reference implementation.
2026-08-20 08:57:20 -07:00
Mitchell Hashimoto
af150144e2 terminal: clear progress bar on full reset (#13901)
Emit a `progress_report` remove effect from the `full_reset` arm,
matching kitty and WezTerm which both clear progress on reset.

Previously, only the termio `StreamHandler` removed the progress bar on
RIS (#10178) - but the terminal stream handler used by `libghostty-vt`
did not, so an embedder's progress bar would outlive the reset.
2026-08-20 08:02:42 -07:00
Mitchell Hashimoto
956a687d63 macOS: refine liquid glass styles (#13928)
Rework for https://github.com/ghostty-org/ghostty/pull/10943;

Keep the background color as it is and apply glass effect on top of it.
Remove the color modification and keep our implementation relatively
simple and more straightforward.

This fixes https://github.com/ghostty-org/ghostty/issues/13914 and also
ofc the linked issue in previous pr.

### Regular


https://github.com/user-attachments/assets/b44e0af1-0a03-4773-8bfa-03ba48804235


https://github.com/user-attachments/assets/83ab0ef5-6791-412d-b260-e7c55e7f890c

### Clear


https://github.com/user-attachments/assets/8970b97c-7866-45dd-a122-dc9c8bdc7ec0


https://github.com/user-attachments/assets/4105dc8f-f531-4a77-b193-b914c46d3075
2026-08-20 06:57:16 -07:00
Mitchell Hashimoto
aea03011d0 termio: release initial input resources (#13931)
Initial input files remained open and their fully read buffers remained
allocated after queueWrite copied their contents. Close these files on
both startup success and failure, free their contents after queueing,
and borrow raw values already owned by the arena.

**AI Usage:** This was spotted by GPT 5.6 Sol. I reworked the code a bit
and understand it all.
2026-08-20 06:56:43 -07:00
Mitchell Hashimoto
0de91eee32 font: render glyf directly into output bitmap (#13929)
Glyf rasterization previously let z2d allocate its alpha surface, then
duplicated the completed pixels into caller-owned storage. Back the z2d
surface with the final bitmap instead, eliminating one allocation and a
full bitmap copy for each non-empty glyph.
2026-08-20 06:54:17 -07:00
Jon Parise
9566a1a87e termio: release initial input resources
Initial input files remained open and their fully read buffers remained
allocated after queueWrite copied their contents. Close these files on
both startup success and failure, free their contents after queueing, and
borrow raw values already owned by the arena.
2026-08-20 09:02:54 -04:00
Jon Parise
6f02d9aad6 font: render glyf directly into output bitmap
Glyf rasterization previously let z2d allocate its alpha surface, then
duplicated the completed pixels into caller-owned storage. Back the z2d
surface with the final bitmap instead, eliminating one allocation and a
full bitmap copy for each non-empty glyph.
2026-08-20 08:12:34 -04:00