Commit Graph

16343 Commits

Author SHA1 Message Date
Mitchell Hashimoto
7092b39445 GTK: Improve Split Close Behavior (#11173)
## Summary
- Adds a "Close Split" option to the right-click context menu in the
split submenu
- Allows users to close the focused split pane directly from the context
menu

Reference discussion:
https://github.com/ghostty-org/ghostty/discussions/10982
2026-06-06 14:02:16 -07:00
Mitchell Hashimoto
f146db5535 terminal: hook up glyph protocol glossary to terminal state (#12937)
This hooks up the glyph protocol glossary to the terminal state. This
effectively makes us handle the APC protocol for it both in Ghostty GUI
and libghostty, although we didn't implement the renderer yet.

The Zig/C libghostty API also has a way to disable the protocol but it
is enabled by default. The memory usage is bound by the specification.

For dirty tracking for the renderer, we're going with the simple route
that any glyph change marks a coarse grained dirty flag and we'll [in
the future] rebuild the entire state in the renderer. I think this will
be fine for realistic workloads, but we can reassess in the future when
we have real workloads.
2026-06-05 19:47:46 -07:00
Mitchell Hashimoto
a979b8698b terminal: hook up glyph protocol glossary to terminal state
This hooks up the glyph protocol glossary to the terminal state. This
effectively makes us handle the APC protocol for it both in Ghostty GUI
and libghostty, although we didn't implement the renderer yet.

The Zig/C libghostty API also has a way to disable the protocol but it is 
enabled by default. The memory usage is bound by the specification.

For dirty tracking for the renderer, we're going with the simple route that 
any glyph change marks a coarse grained dirty flag and we'll [in the future]
rebuild the entire state in the renderer. I think this will be fine for 
realistic workloads, but we can reassess in the future when we have
real workloads.
2026-06-05 15:36:52 -07:00
Mitchell Hashimoto
ea37f4672c fix(terminal): avoid integer overflow in selectPrev with no active matches (#12936)
Also found when test searching.

Run Ghostty debug on macOS and follow these steps:

1. Open Ghostty, `cat src/Surface.zig` and start search
`self.startClipboardRequest`.
2. Click up button(Press enter) 6 times and click down button (Press
shift+enter) 6 times.
3. You should see a panic crash.

### AI Disclosure
Claude implemented the fix and the unit test.

I reviewed it and tested it myself.
2026-06-05 15:10:35 -07:00
Lukas
1e63834cdc fix(terminal): avoid integer overflow in selectPrev with no active matches 2026-06-05 23:30:58 +02:00
Claude Opus 4.8
bd365e1aa9 terminal: cover selection drop when all matches disappear
selectPrev's wrap (active_len + history_len - 1) would underflow if a
selection were live while both result lists are empty. Add a test that
exercises the invariant making that unreachable: overwriting the only match
forces a reload that empties both lists and drops the selection, so the next
select() hits the no-matches guard instead of the wrap arithmetic.
2026-06-05 23:21:27 +02:00
Lukas
d6494544cf terminal: add panic test for integer overflow in selectPrev with no active matches 2026-06-05 23:15:15 +02:00
Mitchell Hashimoto
5a1edfb254 fix(terminal): guard wrap count when resize pushes cursor to scrollback (#12935)
Found this issue when testing some search features; follow up for
#12907.

You can either reproduce using the PoC below with `libghostty-vt` or run
Ghostty debug on macOS and follow these steps:

1. Open Ghostty and start search `0`.
2. Press `cmd+=` to increase font size.
3. You should see a panic crash.

### AI Disclosure

As the commit suggests, Claude implemented the fix, the unit test, and
PoC file.

I reviewed it(seems reasonable to me, but I’m not a Zig professional)
and tested it myself.


```zig
// PoC: resize panic when shrinking both axes with the cursor near the top
// of a fully-populated screen.
//
// Build (with libghostty-vt headers + dylib on the standard search paths):
//   zig run poc.zig -lghostty-vt
//
// Or point at a local build:
//   zig run poc.zig -I <prefix>/include -L <prefix>/lib -lghostty-vt
//
// At runtime the dylib must be discoverable (DYLD_LIBRARY_PATH on macOS,
// LD_LIBRARY_PATH on Linux, or an rpath baked in at link time).
//
// Without the fix, this aborts with
//   reached unreachable code   (assert in PageList.Pin.pageIterator)
// at _terminal.PageList.resizeCols on a debug/safe build. On release it
// silently iterates an empty (reversed) range.

const std = @import("std");
const c = @cImport({
    @cInclude("ghostty/vt.h");
});

pub fn main() !void {
    var term: c.GhosttyTerminal = null;
    const opts: c.GhosttyTerminalOptions = .{
        .cols = 80,
        .rows = 24,
        .max_scrollback = 1000,
    };
    if (c.ghostty_terminal_new(null, &term, opts) != c.GHOSTTY_SUCCESS) {
        return error.InitFailed;
    }
    defer c.ghostty_terminal_free(term);

    // Fill every one of the 24 active rows with non-blank content. This is
    // what makes the bug reachable: when rows shrink, resizeWithoutReflow
    // can only trim *blank* trailing rows, so non-blank rows are instead
    // pushed up into scrollback and the active-area top moves down.
    {
        var buf: [256]u8 = undefined;
        var i: usize = 0;
        while (i < 24) : (i += 1) {
            // "X" on each row; CR+LF between rows but not after the last so
            // we don't scroll the top row away.
            const line = if (i + 1 < 24)
                std.fmt.bufPrint(&buf, "X\r\n", .{}) catch unreachable
            else
                std.fmt.bufPrint(&buf, "X", .{}) catch unreachable;
            c.ghostty_terminal_vt_write(term, line.ptr, line.len);
        }
    }

    // CSI 1;1H -> park the cursor on the TOP row (1-based). The active area is
    // anchored to the bottom, so once we shrink rows this row falls above the
    // new active-area top, i.e. into scrollback.
    const move = "\x1b[1;1H";
    c.ghostty_terminal_vt_write(term, move.ptr, move.len);

    // Shrink both axes. Columns must shrink to take resize()'s .lt branch,
    // which runs the row shrink first and then resizeCols with the original
    // (now out-of-active-area) cursor pin. Panics in
    // _terminal.PageList.resizeCols.
    _ = c.ghostty_terminal_resize(term, 79, 20, 8, 16);

    std.debug.print("survived resize (fix is present)\n", .{});
}
```
2026-06-05 13:56:58 -07:00
Mitchell Hashimoto
179fe571d7 terminal: Glyph Protocol Glossary and request handler implementation (#12930)
This adds the glossary and request handler logic to the glyph protocol
package.

We now have a fully spec compliant business-logic part of the glyph
protocol.

**This doesn't yet hook it up to terminal state.** So it isn't impacting
any real-world usage yet.

Code was hand-written, tests were AI-assisted and human reviewed.
2026-06-05 13:55:42 -07:00
Mitchell Hashimoto
aab0f8079f Revert "font: add exact glyph protocol constraints"
This reverts commit b661adad2e.
2026-06-05 13:44:43 -07:00
Mitchell Hashimoto
b661adad2e font: add exact glyph protocol constraints
Extend glyph render constraints with cell-span sizing modes for height,
width, contain, cover bounds, and stretch bounds. These preserve the
existing face-targeted behavior for platform fonts, emoji, and Nerd Font
rules while giving registered glyphs a target based on terminal cell
spans.

Map Glyph Protocol registration options to the new constraint modes so
sizing follows the spec formulas based on authored advance width and line
height. Baseline alignment now places design-space y=0 on the terminal
text baseline instead of approximating it as start alignment.

Document the placement formulas in the local protocol summary and add
focused tests for constraint mapping, cell-span padding, line-height and
advance scaling, contain versus cover behavior, stretch, and baseline
placement.
2026-06-05 13:37:10 -07:00
Mitchell Hashimoto
e45f002d1a terminal/apc: reject malformed glyph register input
Register parsing now validates the full register request shape before
constructing the parsed command. Inputs that only contain the verb
separator, such as `r`, `r;cp=e0a0`, or `r;foo`, now fail with
InvalidFormat instead of reaching Register invariants guarded by asserts.

Valid empty-payload requests still parse when they include the payload
separator, allowing execution to report malformed_payload through the
normal protocol response path.
2026-06-05 13:19:00 -07:00
Mitchell Hashimoto
f0d81f15ee terminal/apc: reject malformed glyph clear cp
Glyph clear execution previously treated an unparsable cp option the same
as an omitted cp option. That made inputs such as c;cp=zz behave like a
bare clear request and remove every glossary registration.

Track clear option presence separately from successful decoding. A
present but malformed cp now returns a malformed_payload clear failure
without mutating the glossary, while an omitted cp still clears all
registrations.
2026-06-05 13:13:04 -07:00
Mitchell Hashimoto
05eeb43942 font: exclude libghostty-vt from embedded font tests 2026-06-05 13:09:09 -07:00
Claude Opus 4.8
7837563ed6 fix(terminal): guard wrap count when resize pushes cursor to scrollback
In the column-shrink (.lt) branch of PageList.resize, resizeWithoutReflow
lowers self.rows before resizeCols runs. Because the active area is anchored
to the bottom, shrinking rows moves the active-area top down; a cursor near
the top of the old active area then ends up above the new active area (in
scrollback).

resizeCols counts wrap continuations from the cursor pin up to the active-area
top via a .left_up rowIterator. When the cursor pin is above the limit, the
range is reversed and the iterator's order assertion fires (SIGABRT in debug;
silently iterates empty in release).

Count zero wraps when the cursor pin is above the active area, mirroring the
post-reflow preserved-cursor block which already no-ops for a non-active
cursor. Add a regression test.
2026-06-05 21:03:11 +02:00
Mitchell Hashimoto
1c0aac54bd font: reshuffle glyph sizing types to Glyph.zig 2026-06-05 06:58:32 -07:00
Mitchell Hashimoto
d271b271f9 terminal/glyph: query 2026-06-05 06:39:22 -07:00
Mitchell Hashimoto
6f83d8a4f1 terminal/glyph: clear 2026-06-05 06:34:53 -07:00
Mitchell Hashimoto
cc91940993 terminal/glyph: register request 2026-06-05 06:32:41 -07:00
Mitchell Hashimoto
0cd815f94a terminal/apc: glyph glossary delete, contains, clear 2026-06-05 06:23:48 -07:00
Mitchell Hashimoto
cf548a3aad terminal/apc: glyph glossary registration business logic 2026-06-04 18:44:44 -07:00
Mitchell Hashimoto
59d2ad9b6a terminal: glyph protocol Glossary entry starting to take shape 2026-06-04 18:23:08 -07:00
Mitchell Hashimoto
2055bb6dd6 terminal: glyph request glyf decode 2026-06-04 12:02:13 -07:00
Mitchell Hashimoto
42fcd58dba libghostty-vt: add options to configure default cursor's style and blink (#12900)
This PR adds 2 options to `libghostty-vt` to configure the style and
blink status of the default cursor. They control how the terminal
renders the cursor when a program doesn't request any explicit style or
when it resets it to the terminal's default state by sending a DECSCUSR
reset sequence (`CSI 0 q`).
2026-06-04 11:39:50 -07:00
Mitchell Hashimoto
8fcead00e5 font: glyf outline decoder and rasterizer (#12893)
This adds a Glyf outline decoder and rasterizer.

So it turns out that FreeType and CoreText have very shitty APIs for raw
Glyf table rasterization. CoreText as far as I can find can't do it at
all. In both cases you have to create a synthetic font with just this
entry and rasterize the glyph. And the code to do all that was WAYYYYYY
complex such that this made way more sense.

We need this for the Glyph Protocol.

**AI disclosure:** Hand-written parser, rasterizer. AI assisted
validation and test writing. I read the spec myself.

cc @qwerasd205
2026-06-04 11:04:19 -07:00
Mitchell Hashimoto
52368cbcff core: send selection_changed notification (#12902)
The core had no signal to the apprt when the active selection changed,
so a consumer (e.g. a screen reader) kept reading a stale selection
until some unrelated query refreshed it.

This change adds a payload-less selection_changed action that's fired on
a selection state transition. The apprt reads the current selection
through the normal read path.

This consolidates selection state changes so the notification fires
consistently: all sites route through setSelection rather than calling
screen.select directly, including the mouse paths that previously
bypassed it for clipboard timing.

The new setSelectionAndCopy extends setSelection with the additional
'copy_on_select' behavior.

On macOS, this posts .ghosttySelectionDidChange, which is debounced
before posting a NSAccessibility .selectedTextChanged notification.

GTK has no consumer yet and no-ops the action.

See: #9932
2026-06-04 11:03:59 -07:00
Mitchell Hashimoto
4782e59eac terminal: saturate cursor subtraction in resizeCols (#12907)
PageList.resize takes the .lt branch when columns shrink, which calls
resizeWithoutReflow (mutating self.rows to the new smaller value) and
then resizeCols with the original opts.cursor.y. When both axes shrink
in one call and the cursor sits at or past the new bottom row, the
expression `self.rows - c.y - 1` underflows and panics in safety builds.

Use saturating subtraction; "remaining rows below cursor" is 0 once the
cursor sits at or past the new bottom.

This problem is reported by
[discussion#12905](https://github.com/ghostty-org/ghostty/discussions/12905)
2026-06-04 11:02:41 -07:00
Jeffrey C. Ollie
bfe633a948 build(deps): bump actions/checkout from 6.0.2 to 6.0.3 (#12911)
Bumps [actions/checkout](https://github.com/actions/checkout) from 6.0.2
to 6.0.3.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/actions/checkout/releases">actions/checkout's
releases</a>.</em></p>
<blockquote>
<h2>v6.0.3</h2>
<h2>What's Changed</h2>
<ul>
<li>Update changelog by <a
href="https://github.com/ericsciple"><code>@​ericsciple</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/2357">actions/checkout#2357</a></li>
<li>fix: expand merge commit SHA regex and add SHA-256 test cases by <a
href="https://github.com/yaananth"><code>@​yaananth</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/2414">actions/checkout#2414</a></li>
<li>Fix checkout init for SHA-256 repositories by <a
href="https://github.com/yaananth"><code>@​yaananth</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/2439">actions/checkout#2439</a></li>
<li>Update changelog for v6.0.3 by <a
href="https://github.com/yaananth"><code>@​yaananth</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/2446">actions/checkout#2446</a></li>
</ul>
<h2>New Contributors</h2>
<ul>
<li><a href="https://github.com/yaananth"><code>@​yaananth</code></a>
made their first contribution in <a
href="https://redirect.github.com/actions/checkout/pull/2414">actions/checkout#2414</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/actions/checkout/compare/v6...v6.0.3">https://github.com/actions/checkout/compare/v6...v6.0.3</a></p>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/actions/checkout/blob/main/CHANGELOG.md">actions/checkout's
changelog</a>.</em></p>
<blockquote>
<h1>Changelog</h1>
<h2>v6.0.3</h2>
<ul>
<li>Fix checkout init for SHA-256 repositories by <a
href="https://github.com/yaananth"><code>@​yaananth</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/2439">actions/checkout#2439</a></li>
<li>fix: expand merge commit SHA regex and add SHA-256 test cases by <a
href="https://github.com/yaananth"><code>@​yaananth</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/2414">actions/checkout#2414</a></li>
</ul>
<h2>v6.0.2</h2>
<ul>
<li>Fix tag handling: preserve annotations and explicit fetch-tags by <a
href="https://github.com/ericsciple"><code>@​ericsciple</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/2356">actions/checkout#2356</a></li>
</ul>
<h2>v6.0.1</h2>
<ul>
<li>Add worktree support for persist-credentials includeIf by <a
href="https://github.com/ericsciple"><code>@​ericsciple</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/2327">actions/checkout#2327</a></li>
</ul>
<h2>v6.0.0</h2>
<ul>
<li>Persist creds to a separate file by <a
href="https://github.com/ericsciple"><code>@​ericsciple</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/2286">actions/checkout#2286</a></li>
<li>Update README to include Node.js 24 support details and requirements
by <a href="https://github.com/salmanmkc"><code>@​salmanmkc</code></a>
in <a
href="https://redirect.github.com/actions/checkout/pull/2248">actions/checkout#2248</a></li>
</ul>
<h2>v5.0.1</h2>
<ul>
<li>Port v6 cleanup to v5 by <a
href="https://github.com/ericsciple"><code>@​ericsciple</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/2301">actions/checkout#2301</a></li>
</ul>
<h2>v5.0.0</h2>
<ul>
<li>Update actions checkout to use node 24 by <a
href="https://github.com/salmanmkc"><code>@​salmanmkc</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/2226">actions/checkout#2226</a></li>
</ul>
<h2>v4.3.1</h2>
<ul>
<li>Port v6 cleanup to v4 by <a
href="https://github.com/ericsciple"><code>@​ericsciple</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/2305">actions/checkout#2305</a></li>
</ul>
<h2>v4.3.0</h2>
<ul>
<li>docs: update README.md by <a
href="https://github.com/motss"><code>@​motss</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/1971">actions/checkout#1971</a></li>
<li>Add internal repos for checking out multiple repositories by <a
href="https://github.com/mouismail"><code>@​mouismail</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/1977">actions/checkout#1977</a></li>
<li>Documentation update - add recommended permissions to Readme by <a
href="https://github.com/benwells"><code>@​benwells</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/2043">actions/checkout#2043</a></li>
<li>Adjust positioning of user email note and permissions heading by <a
href="https://github.com/joshmgross"><code>@​joshmgross</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/2044">actions/checkout#2044</a></li>
<li>Update README.md by <a
href="https://github.com/nebuk89"><code>@​nebuk89</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/2194">actions/checkout#2194</a></li>
<li>Update CODEOWNERS for actions by <a
href="https://github.com/TingluoHuang"><code>@​TingluoHuang</code></a>
in <a
href="https://redirect.github.com/actions/checkout/pull/2224">actions/checkout#2224</a></li>
<li>Update package dependencies by <a
href="https://github.com/salmanmkc"><code>@​salmanmkc</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/2236">actions/checkout#2236</a></li>
</ul>
<h2>v4.2.2</h2>
<ul>
<li><code>url-helper.ts</code> now leverages well-known environment
variables by <a href="https://github.com/jww3"><code>@​jww3</code></a>
in <a
href="https://redirect.github.com/actions/checkout/pull/1941">actions/checkout#1941</a></li>
<li>Expand unit test coverage for <code>isGhes</code> by <a
href="https://github.com/jww3"><code>@​jww3</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/1946">actions/checkout#1946</a></li>
</ul>
<h2>v4.2.1</h2>
<ul>
<li>Check out other refs/* by commit if provided, fall back to ref by <a
href="https://github.com/orhantoy"><code>@​orhantoy</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/1924">actions/checkout#1924</a></li>
</ul>
<h2>v4.2.0</h2>
<ul>
<li>Add Ref and Commit outputs by <a
href="https://github.com/lucacome"><code>@​lucacome</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/1180">actions/checkout#1180</a></li>
<li>Dependency updates by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>- <a
href="https://redirect.github.com/actions/checkout/pull/1777">actions/checkout#1777</a>,
<a
href="https://redirect.github.com/actions/checkout/pull/1872">actions/checkout#1872</a></li>
</ul>
<h2>v4.1.7</h2>
<ul>
<li>Bump the minor-npm-dependencies group across 1 directory with 4
updates by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/1739">actions/checkout#1739</a></li>
<li>Bump actions/checkout from 3 to 4 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/1697">actions/checkout#1697</a></li>
<li>Check out other refs/* by commit by <a
href="https://github.com/orhantoy"><code>@​orhantoy</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/1774">actions/checkout#1774</a></li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="df4cb1c069"><code>df4cb1c</code></a>
Update changelog for v6.0.3 (<a
href="https://redirect.github.com/actions/checkout/issues/2446">#2446</a>)</li>
<li><a
href="1cce3390c2"><code>1cce339</code></a>
Fix checkout init for SHA-256 repositories (<a
href="https://redirect.github.com/actions/checkout/issues/2439">#2439</a>)</li>
<li><a
href="900f2210b1"><code>900f221</code></a>
fix: expand merge commit SHA regex and add SHA-256 test cases (<a
href="https://redirect.github.com/actions/checkout/issues/2414">#2414</a>)</li>
<li><a
href="0c366fd6a8"><code>0c366fd</code></a>
Update changelog (<a
href="https://redirect.github.com/actions/checkout/issues/2357">#2357</a>)</li>
<li>See full diff in <a
href="de0fac2e45...df4cb1c069">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=actions/checkout&package-manager=github_actions&previous-version=6.0.2&new-version=6.0.3)](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 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>
2026-06-03 14:26:43 -05:00
dependabot[bot]
5f7738a0e9 build(deps): bump actions/checkout from 6.0.2 to 6.0.3
Bumps [actions/checkout](https://github.com/actions/checkout) from 6.0.2 to 6.0.3.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](de0fac2e45...df4cb1c069)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: 6.0.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-06-03 18:35:02 +00:00
Zongyuan Li
f135b95098 terminal: test shrinking both axes with cursor past new bottom
Adds a PageList regression test exercising the underflow path fixed in
7fa6fffbc, and a libghostty-vt C API test mirroring the original repro
through ghostty_terminal_resize.
2026-06-04 00:01:24 +08:00
Lukas
4df593bd24 macos: fix GHOSTTY_QUICK_TERMINAL not set for quick terminal splits (#12896) 2026-06-03 08:49:00 +02:00
zongyuan.li
7fa6fffbca terminal: saturate cursor subtraction in resizeCols
PageList.resize takes the .lt branch when columns shrink, which calls
resizeWithoutReflow (mutating self.rows to the new smaller value) and
then resizeCols with the original opts.cursor.y. When both axes shrink
in one call and the cursor sits at or past the new bottom row, the
expression `self.rows - c.y - 1` underflows and panics in safety builds.

Use saturating subtraction; "remaining rows below cursor" is 0 once the
cursor sits at or past the new bottom.
2026-06-03 14:36:52 +08:00
ghostty-vouch[bot]
629838b9bd Update VOUCHED list (#12906)
Triggered by [discussion
comment](https://github.com/ghostty-org/ghostty/discussions/12905#discussioncomment-17160340)
from @jcollie.

Vouch: @c0x0o

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-06-03 04:48:36 +00:00
Jon Parise
c4e1ab8883 core: send selection_changed notification
The core had no signal to the apprt when the active selection changed,
so a consumer (e.g. a screen reader) kept reading a stale selection
until some unrelated query refreshed it.

This change adds a payload-less selection_changed action that's fired on
a selection state transition. The apprt reads the current selection
through the normal read path.

This consolidates selection state changes so the notification fires
consistently: all sites route through setSelection rather than calling
screen.select directly, including the mouse paths that previously
bypassed it for clipboard timing.

The new setSelectionAndCopy extends setSelection with the additional
'copy_on_select' behavior.

On macOS, this posts .ghosttySelectionDidChange, which is debounced
before posting a NSAccessibility .selectedTextChanged notification.

GTK has no consumer yet and no-ops the action.
2026-06-02 19:37:49 -04:00
Riccardo Mazzarini
e7b506c69d Test setting/resetting the default cursor style and blink 2026-06-02 18:12:17 +02:00
Riccardo Mazzarini
66950a4a53 libghostty: add option to set default cursor blink
Adds an option to `libghostty-vt` to configure whether the default
cursor displayed when an app sends a DECSCUSR reset sequence should
blink.
2026-06-02 18:03:45 +02:00
Riccardo Mazzarini
2444e4d557 libghostty: add option to set default cursor style
Adds an option to `libghostty-vt` to configure the default cursor style
that should be displayed when an app sends a DECSCUSR reset sequence
(`CSI 0 q`).
2026-06-02 17:57:41 +02:00
Mitchell Hashimoto
51995a7822 font: glyf rasterization png comparison 2026-06-02 06:08:59 -07:00
Mitchell Hashimoto
6246c288ae core: fix use-after-free in Surface.setSelection (#12894)
`setSelection` captured the previous selection, then called
`Screen.select` (which deinits the previous selection's tracked pins),
then compared the new selection against the now-freed previous pin via
`sel.eql(prev)`. That read freed pin memory (use-after-free).

The comparison was a copy-on-select optimization ("only re-copy if the
selection changed"). Remove it rather than repair it because:

- It never fired correctly. It compared against freed memory, so the
shipped behavior was already "always copy".

- It can't be repaired by copying `prev`'s pin before `Screen.select`.
That fixes the use-after-free but not the logic: the call sites (e.g.
mouse drag release) pass a selection equal to the one already set, so a
working `eql` skip would suppress the very copy those sites exist to
perform. A correct optimization would have to compare against the
last-copied selection (before the mouse event mutated the live one),
which would require extra state.

- It isn't worth tracking that additional state. The copy runs once per
selection gesture (mouse up, double-click), which isn't in a hot path,
so skipping a redundant re-copy only saves a single clipboard write.

Removing the skip eliminates the use-after-free and keeps the behavior
consistent with what we've already been doing.

---

_AI Disclosure_: Claude Opus 4.8 found this in a review while I was
working on adjacent code.
2026-06-02 06:07:31 -07:00
YuWiz
ef68e96400 macos: fix GHOSTTY_QUICK_TERMINAL not set for quick terminal splits 2026-06-02 16:48:36 +08:00
Mitchell Hashimoto
8eff74ef76 font: add glyf rasterizer 2026-06-01 20:38:02 -07:00
Jon Parise
76b9bdb199 terminal: test Screen.select frees existing pins 2026-06-01 20:09:25 -04:00
Jon Parise
ab82b8ab72 core: fix use-after-free in Surface.setSelection
setSelection captured the previous selection, then called Screen.select
(which deinits the previous selection's tracked pins), then compared the
new selection against the now-freed previous pin via `sel.eql(prev)`.
That read freed pin memory (use-after-free).

The comparison was a copy-on-select optimization ("only re-copy if the
selection changed"). Remove it rather than repair it because:

- It never fired correctly. It compared against freed memory, so the
  shipped behavior was already "always copy".

- It can't be repaired by copying `prev`'s pin before Screen.select.
  That fixes the use-after-free but not the logic: the call sites (e.g.
  mouse drag release) pass a selection equal to the one already set, so
  a working `eql` skip would suppress the very copy those sites exist to
  perform. A correct optimization would have to compare against the
  last-copied selection (before the mouse event mutated the live one),
  which would require extra state.

- It isn't worth tracking that additional state. The copy runs once per
  selection gesture (mouse up, double-click), which isn't in a hot path,
  so skipping a redundant re-copy only saves a single clipboard write.

Removing the skip eliminates the use-after-free and keeps the behavior
consistent with what we've already been doing.
2026-06-01 20:09:14 -04:00
Mitchell Hashimoto
d8f56b790e font: add glyf entry decoder to outline
Add Glyf.Outline for decoding the contours and points of a Glyf.
2026-06-01 14:52:23 -07:00
Mitchell Hashimoto
5758e14931 terminal: glyph protocol parser and response encoder (#12352)
**Important: this DOES NOT hook up the glyph protocol to Ghostty or
libghostty. Its just the parser.**

This adds the core parse/encode for the still in-development and
experimental terminal glyph protocol:
https://github.com/raphamorim/rio/pull/1542

The only cross-cutting change necessary was changing the APC
identification logic which previously only looked at a single byte to
support multi-byte identifiers since the glyph protocol uses `25a1`.

For DoS protection, the default limits any glyph-related APC command
size to 1 megabyte.

> [!WARNING]
> 
> Since this protocol is still in development and discussion, there is
no promise the implementation will stay within Ghostty or that any of
the APIs exposed by this will remain stable. We're just getting ahead of
it.
2026-06-01 10:57:52 -07:00
Mitchell Hashimoto
d3775d1ed0 terminal: glyph protocol parser and response encoder
This adds the core parse/encode for the still in-development and experimental
terminal glyph protocol: https://github.com/raphamorim/rio/pull/1542
Up to version 1.9.

The only cross-cutting change necessary was changing the APC
identification logic which previously only looked at a single byte to
support multi-byte identifiers since the glyph protocol uses `25a1`.
2026-06-01 10:50:05 -07:00
Mitchell Hashimoto
43e0340175 Update iTerm2 colorschemes (#12867)
Upstream release:
https://github.com/mbadolato/iTerm2-Color-Schemes/releases/tag/release-20260525-155808-7335c0a
2026-06-01 10:03:09 -07:00
Mitchell Hashimoto
b81670f3f4 macOS: mark Swift os.Logger interpolations as public (#12877)
### AI Disclosure

Claude implemented it. I'm fully aware of and confident about the
change; it's just chore work actually.
2026-06-01 10:02:53 -07:00
ghostty-vouch[bot]
0f7cd84b88 Update VOUCHED list (#12889)
Triggered by [discussion
comment](https://github.com/ghostty-org/ghostty/discussions/12840#discussioncomment-17132417)
from @bo2themax.

Vouch: @52dyd

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-06-01 08:07:04 +00:00
Jeffrey C. Ollie
16f2fdc90c config: fix missing space in docs (#12879)
fixes #12873

comment/docs only change:
switched space and tab in default value of `selection-word-chars` so
there is no space at the value boundary
needed because markdown trims spaces at the beginning & end of a code
snippet
2026-05-31 12:00:12 -05:00