Commit Graph

13931 Commits

Author SHA1 Message Date
Lars
bfe5a4be8f move config loading to Config 2026-01-20 09:15:14 -08:00
Lukas
32562e0c98 Update macos/Sources/Ghostty/Ghostty.App.swift
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-01-20 09:15:14 -08:00
Lars
35b2e820ce typo fix 2026-01-20 09:15:14 -08:00
Lars
4154a730ee Fix some edge cases 2026-01-20 09:15:14 -08:00
Lars
27729bf8a7 only run ui tests manually with xcode 2026-01-20 09:15:14 -08:00
Lars
85da70c98d Add more theme test cases from #9360 2026-01-20 09:15:14 -08:00
Lars
4aabd94716 add theme tests 2026-01-20 09:15:14 -08:00
Lars
d9ed325818 update config explicitly 2026-01-20 09:15:14 -08:00
Lars
6ada9c7844 test: add test for mergeAllWindows 2026-01-20 09:15:14 -08:00
Lars
f52013787e test: using XCTAssertEqual to get more information 2026-01-20 09:15:14 -08:00
Lars
f088ce38d9 test: read config before launching XCUIApplication 2026-01-20 09:15:14 -08:00
Lars
f7608d0b95 macOS: reduce press duration 2026-01-20 09:15:14 -08:00
Lars
b1290dc47b macOS: add titlebar tabs tests 2026-01-20 09:15:14 -08:00
Lars
bcf42dde6c build: change deployment target 2026-01-20 09:15:14 -08:00
Lars
a94a6e4b36 build: fix Ghostty-iOS compiling 2026-01-20 09:15:14 -08:00
Lars
4f667520fa macOS: GhosttyUITests 2026-01-20 09:15:14 -08:00
Lars
9c5d4a5511 ghosttyKit: add ghostty_config_load_file 2026-01-20 09:15:14 -08:00
Mitchell Hashimoto
9ec6e9ea9a terminal: page split and compact operations, more robust OutOfSpace handling (#10383)
Fixes #10357

This updates `manualStyleUpdate` in `Screen` to perform a page split if
we get an `OutOfSpace` error. A page split will choose the half that has
less used capacity and split to that side, hoping we'll now have space
for adding our style. Previously, we'd just raise this error up and not
do any splitting.

Callers of `manualStyleUpdate` include `setAttribute` amongst many
others. So a significant part of libghostty is now resilient to
OutOfSpace.

This also updates `restoreCursor` to no longer ever fail. If style
updates fail for restore cursor, we revert back to a default (unstyled)
cursor. This is important because terminal programs that send a restore
cursor sequence have no way to understand the restoration failed, and
its more important that we restored as much as possible (position and so
on) then restore everything in it.

## Internals

This adds two new PageList operations:

* `split` - Splits a page at a given row, moving rows at and after the
split point to a new page. Used when a page runs out of capacity and we
need to split it to continue operations.
* `compact` - Compacts a page to use the minimum required memory.
Computes exact capacity needed for page contents and creates a smaller
page if meaningful savings are possible. Clones the data into it,
updating all metadata like tracked pins. **This isn't used yet! But it
is well tested.**

And a supporting Page operation:

* `Page.exactRowCapacity` - Computes the exact capacity required to
store a range of rows from a page by counting unique styles, hyperlinks,
grapheme bytes, and string bytes. This takes into account load factors
and so on.

## Weaknesses

* `manualStyleUpdate` only splits once. For maximum robustness we should
probably recursively split to try to make space until we're down to a 1
row page. I didn't want to do this without compaction implemented though
cause pathological cases could cause crazy memory blow-up.
* `split` duplicates the capacity, effectively doubling memory when it
happens (wasted capacity). I think `compact` calling should be done
somewhere, which is why I implemented it, but didn't want to integrate
too much in one PR so we can get some real world testing...
* `compact` can't go smaller than `std_size` due to the way PageList
works. We may want to consider making a MUCH smaller `std_size` and
leaning in to more non standard pages.

## AI Disclosure

I used Amp a lot to help in every aspect of this work. I reviewed every
line written including tests and did significant manual modification
too.
2026-01-19 13:28:55 -08:00
Mitchell Hashimoto
ae8d2c7a3e terminal: fix up some of the manual handling, comments 2026-01-19 12:17:09 -08:00
Mitchell Hashimoto
a8b31ceb84 terminal: restoreCursor is now longer fallible
We need to have sane behavior in error handling because the running
program that sends the restore cursor command has no way to realize it
failed. So if our style fails to add (our only fail case) then we revert
to no style.

https://ampcode.com/threads/T-019bd7dc-cf0b-7439-ad2f-218b3406277a
2026-01-19 12:12:19 -08:00
Mitchell Hashimoto
4244c38bea macos: support mouse buttons 8/9 (back/forward) (#10381)
Add support for mouse buttons 4-11 in the macOS app. Previously only
left, right, and middle buttons were handled. Now otherMouseDown/Up
events properly map NSEvent.buttonNumber to the corresponding Ghostty
mouse button, enabling back/forward button support.

Fixes: https://github.com/ghostty-org/ghostty/issues/2425
Amp-Thread-ID:
https://ampcode.com/threads/T-019bd74e-6b2b-731d-b43a-ac73b3460c32
2026-01-19 12:08:38 -08:00
Mitchell Hashimoto
c412b30cb5 terminal: splitForCapacity, manualStyleUpdate uses this 2026-01-19 11:59:00 -08:00
Mitchell Hashimoto
4e60a85099 terminal: setAttribute failure reverts back to prior style 2026-01-19 11:52:39 -08:00
Mitchell Hashimoto
93a070c6de terminal: PageList split operation 2026-01-19 11:52:37 -08:00
Tim Culverhouse
5ee56409c7 macos: support mouse buttons 8/9 (back/forward)
Add support for mouse buttons 4-11 in the macOS app. Previously only
left, right, and middle buttons were handled. Now otherMouseDown/Up
events properly map NSEvent.buttonNumber to the corresponding Ghostty
mouse button, enabling back/forward button support.

Fixes: https://github.com/ghostty-org/ghostty/issues/2425
Amp-Thread-ID: https://ampcode.com/threads/T-019bd74e-6b2b-731d-b43a-ac73b3460c32
Co-authored-by: Amp <amp@ampcode.com>
2026-01-19 11:59:50 -06:00
Mitchell Hashimoto
f9699eceb0 terminal: PageList.compact 2026-01-19 09:18:58 -08:00
Mitchell Hashimoto
93436217c8 terminal: page.exactRowCapacity 2026-01-19 09:16:49 -08:00
Mitchell Hashimoto
d6cb84d12f terminal: remap tracked pins in backfill optimization path during resize (#10377)
Fixes #10369

When `resizeWithoutReflowGrowCols` copies rows to a previous page with
spare capacity, tracked pins pointing to those rows were not being
remapped. This left pins pointing to the original page which was
subsequently destroyed.

The fix adds pin remapping for rows copied to the previous page,
matching the existing remapping logic for rows copied to new pages.

I also added new integrity checks to verify that our tracked pins are
always valid at points where internal operations complete.

Thanks to @grishy for finding this!

**AI disclosure:** Amp used for verifying and fixing this bug. I
reviewed the results and just did minor manual tweaks.
https://ampcode.com/threads/T-019bd6d7-0645-73dd-8fd7-659f019fa83d and
https://ampcode.com/threads/T-019bd6c9-cc2e-73bc-bbaa-f8766e11c234
2026-01-19 09:15:09 -08:00
Mitchell Hashimoto
d67dd08a21 terminal: remap tracked pins in backfill path during resize
Fixes #10369

When `resizeWithoutReflowGrowCols` copies rows to a previous page with
spare capacity, tracked pins pointing to those rows were not being remapped. 
This left pins pointing to the original page which was subsequently destroyed.

The fix adds pin remapping for rows copied to the previous page,
matching the existing remapping logic for rows copied to new pages.

I also added new integrity checks to verify that our tracked pins are
always valid at points where internal operations complete.

Thanks to @grishy for finding this!
2026-01-19 09:11:49 -08:00
Jeffrey C. Ollie
9f16b8d112 osc 133: handle bare keys with no '=' (#10380)
Fixes #10379
2026-01-19 11:11:29 -06:00
Jeffrey C. Ollie
1daba40247 osc 133: handle bare keys with no '='
Fixes #10379
2026-01-19 10:59:00 -06:00
Mitchell Hashimoto
250877eff6 terminal: increaseCapacity should preserve dirty flag (#10374)
This never caused any known issues, but it's a bug! `increaseCapacity`
should produce a node with identical contents, just more capacity. We
were forgetting to copy over the dirty flag.

I looked back at `adjustCapacity` and it also didn't preserve the dirty
flag so presumably downstream consumers have been handling this case
manually. But, I think semantically it makes sense for
`increaseCapacity` to preserve the dirty flag.

This bug was found by AI (while I was doing another task). I fixed it
and wrote the test by hand though.
2026-01-18 14:52:56 -08:00
Mitchell Hashimoto
8d78a82d1b ssh-cache: use AtomicFile to write the cache file (#10365)
We previously wrote our new cache file into a temporary directory and
the (atomically) renamed it to the canonical cache file path. This
rename operation unfortunately only works when both files are on the
same file system, and that's not always the case (e.g. when $TMPDIR is
on its own file system).

Instead, we can use Zig's AtomicFile to safely perform this operation
inside of the cache directory.

There's a new risk of a crash leaving the temporary file around in this
directory (and not getting cleaned up like $TMPDIR-based files), but the
probability is low and those files will only be readable by the creating
user (mode 0o600).

There's a new test cash that verifies the expected AtomicFile clean up
behavior. I also switched the file-oriented tests to use testing.tmpDir
rather than using our application-level TempDir type.

Fixes #10364

---

**AI Disclosure:** I asked Claude to write the initial test case to
verify the AtomicFile cleanup behavior.
2026-01-18 14:52:30 -08:00
Mitchell Hashimoto
676b4993e8 Update iTerm2 colorschemes (#10363)
Upstream release:
https://github.com/mbadolato/iTerm2-Color-Schemes/releases/tag/release-20260112-150707-28c8f5b
2026-01-18 14:51:50 -08:00
Mitchell Hashimoto
3ee30058ab terminal: increaseCapacity should preserve dirty flag
This never caused any known issues, but it's a bug! `increaseCapacity`
should produce a node with identical contents, just more capacity. We
were forgetting to copy over the dirty flag.

I looked back at `adjustCapacity` and it also didn't preserve the dirty
flag so presumably downstream consumers have been handling this case
manually. But, I think semantically it makes sense for
`increaseCapacity` to preserve the dirty flag.

This bug was found by AI (while I was doing another task). I fixed it
and wrote the test by hand though.
2026-01-18 14:47:12 -08:00
Jon Parise
5423d64c6a ssh-cache: use AtomicFile to write the cache file
We previously wrote our new cache file into a temporary directory and
the (atomically) renamed it to the canonical cache file path. This
rename operation unfortunately only works when both files are on the
same file system, and that's not always the case (e.g. when $TMPDIR is
on its own file system).

Instead, we can use Zig's AtomicFile to safely perform this operation
inside of the cache directory.

There's a new risk of a crash leaving the temporary file around in this
directory (and not getting cleaned up like $TMPDIR-based files), but the
probability is low and those files will only be readable by the creating
user (mode 0o600).

There's a new test cash that verifies the expected AtomicFile clean up
behavior. I also switched the file-oriented tests to use testing.tmpDir
rather than using our application-level TempDir type.
2026-01-17 20:44:22 -05:00
mitchellh
73a8d64b8a deps: Update iTerm2 color schemes 2026-01-18 00:16:59 +00:00
Mitchell Hashimoto
9fb03ba55c terminal: grow prune check should not prune if required for active (#10353)
Fixes #10352

The bug was that non-standard pages would mix the old
`growRequiredForActive` check and make our active area insufficient in
the PageList.

But, since scrollbars now require we have a cached `total_rows` that our
safety checks always verify, we can remove the old linked list traversal
and switch to some simple math in general across all page sizes.
2026-01-16 21:34:16 -08:00
Mitchell Hashimoto
464c31328e terminal: grow prune check should not prune if required for active
Fixes #10352

The bug was that non-standard pages would mix the old
`growRequiredForActive` check and make our active area insufficient in
the PageList.

But, since scrollbars now require we have a cached `total_rows` that our
safety checks always verify, we can remove the old linked list traversal
and switch to some simple math in general across all page sizes.
2026-01-16 21:26:54 -08:00
Mitchell Hashimoto
56237efeef PageList overflow detection and protection (#10337)
Fixes #10258  
Replaces #10284

1. `Page.Capacity` now uses smaller bit-width integers that represent a
true maximum capacity for various fields.
2. On 64-bit systems, a maxed out `Page.Capacity` (every field `maxInt`)
can be represented in an addressable allocation (total required memory
less than 64 bits). This means `Page.layout` can't overflow.
3. All `adjustCapacity` functions replaced with `increaseCapacity` which
doesn't allow specifying the resulting value, which makes it so overflow
is only possible in significantly fewer places, making it easier to
handle in general.
4. `increaseCapacity` can return a new error `OutOfSpace` which happens
when overflow is detected. This means that no valid page can accommodate
the desired capacity increase because we're already at the max. The
caller is expected to handle this.
5. Updated our resize so that the only possible error is system OOM, we
handle the new `OutOfSpace` by copying the recent reflowed row into a
new page and continuing.

A very, very high-level overview is below. The "overflow" here papers
over a bunch of details where the prior usize capacities flowed through
to Page.layout and ultimately RefCountedSet and other managed types
which then caused incorrect calculations on total memory size required.

```mermaid
flowchart TB
    subgraph Before["Before: adjustCapacity"]
        A1[capacity: usize] --> A2["capacity *= 2"]
        A2 --> A3{Overflow?}
        A3 -->|"Not detected"| A4["Massive allocation or crash"]
    end
    
    subgraph After["After: increaseCapacity"]
        B1["capacity: bounded int<br/>(u16/u32)"] --> B2["capacity *= 2"]
        B2 --> B3{Overflow?}
        B3 -->|"OutOfSpace error"| B4["Graceful handling:<br/>move row to new page"]
        B3 -->|"Success"| B5["Normal allocation"]
    end
    
    Before --> After
    
    classDef beforeStyle fill:#3d1a1a,stroke:#ff6b6b,color:#ff6b6b
    classDef afterStyle fill:#1a3d3a,stroke:#4ecdc4,color:#4ecdc4
    
    class A1,A2,A3,A4 beforeStyle
    class B1,B2,B3,B4,B5 afterStyle
```
2026-01-16 14:52:28 -08:00
Mitchell Hashimoto
df35363b15 terminal: more robust handling of max_page_size
not 100%
2026-01-16 14:47:53 -08:00
Mitchell Hashimoto
442a395850 terminal: ensure our std_capacity fits within max page size 2026-01-16 14:25:01 -08:00
Mitchell Hashimoto
4770aba089 Misc bug fixes (#10349)
Just fixes for a couple bugs I happened across
2026-01-16 14:13:59 -08:00
Qwerasd
69066200ef fix: handle double tmux control mode exit command 2026-01-16 16:58:58 -05:00
Mitchell Hashimoto
85a3d623b2 terminal: increaseCapacity should reach maxInt before overflow 2026-01-16 13:43:56 -08:00
Qwerasd
4e5c1dcdc1 osc: fix bad indexing for empty kv in semantic prompt 2026-01-16 16:36:37 -05:00
Qwerasd
f89b6433c2 osc: add failing test for osc 133 parsing trailing ;
This actually causes a crash lol, bad indexing of a slice with `1..0`
because it's `key.len + 1 ..` and the length is `0`.
2026-01-16 16:29:59 -05:00
Mitchell Hashimoto
ec0a150098 terminal: moveLastRowToNewPage needs to fix up total_rows 2026-01-16 13:23:55 -08:00
Mitchell Hashimoto
c9d15949d8 terminal: on reflow OutOfSpace, move last row to new page and try again 2026-01-16 13:23:55 -08:00
Mitchell Hashimoto
42321cc7d5 terminal: write to the proper cell 2026-01-16 13:23:55 -08:00