Commit Graph

16068 Commits

Author SHA1 Message Date
Jeffrey C. Ollie
8f3d9b4690 update zon2nix to 0.5.0
fix hash outputs for flatpak
build with Zig 0.16 from nixpkgs
(which required fixing prettier reference in devShell.nix)
2026-04-27 12:42:04 -05:00
Mitchell Hashimoto
34cbb5fa81 feat: add middle-click action configuration (#12478)
This PR addresses
https://github.com/ghostty-org/ghostty/discussions/12108 implemented
similarly to https://github.com/ghostty-org/ghostty/pull/8254 to allow
middle click + TrackPoint scrolling on MacOS. `primary-paste` naming
comes from `gtk_enable_primary_paste`.

The following configuration values for `middle-click-action` are
provided:
- `primary-paste` - Paste from the selection (or system) clipboard per
`copy-on-select`.
- `ignore` - Do nothing, ignore the middle click.

Tested locally on macOS with Zig 0.15.2 using `zig build
-Doptimize=ReleaseFast`.

Thank you!
2026-04-27 09:29:29 -07:00
Mitchell Hashimoto
0b56ae2cc7 gtk: fix quick terminal breaking when manually toggled off while auto-hide is enabled (#12471)
Fixes quick terminal breaking when auto-hide is enabled and quick
terminal is manually toggled off (#11679).

`quick-terminal-autohide` is implemented by the `Window.propIsActive`
function in `apprt/gtk/class/window.zig` which calls
`Window.toggleVisibility` when the quick terminal window becomes
inactive (loses focus). However `Window.propIsActive` is also triggered
when you manually hide the quick terminal because hiding it causes the
window to become inactive. Normally that should just toggle the quick
terminal off and immediately back on, but there is also a re-entrancy
issue. Manually toggling off the terminal causes the
`Application.toggleQuickTerminal` (in `apprt/gtk/class/application.zig`)
to run which sets off the call chain `Window.toggleVisibility ->
gtk_widget_set_visible -> ... GTK signal/event handling ... ->
Window.propIsActive -> Window.toggleVisibility ->
gtk_widget_set_visible`.
The nested calls to `gtk_widget_set_visible` cause the GTK window state
to become corrupted. The window is marked visible, but is not actually
visible or just shows a placeholder. What exactly happens depends on the
compositor and how it handles moving window focus.

Reproduced the bug on KDE and hyprland and verified the fix on both.

### Changes

`apprt/gtk/class/window.zig`: added check to `Window.propIsActive` to
only toggle quick-terminal if it is inactive **and** visible.

### AI Disclosure

Found the bug without AI using "printf debugging" then traced it through
GTK with valgrind. Used GPT5.4 in setting up valgrind and researching
how signals/events move through GTK internally.
2026-04-27 09:26:24 -07:00
Mitchell Hashimoto
576d07ffc1 macOS: update tests and add test plan (#12473)
This updates UI tests and adds a test plan on disk, so we can change the
configuration to different ones with the host app.

If you changed the icon in regular ghostty config file, the tests can
only be run once, since the signature is changed after changing the
icon. Adding an on-disk test plan helps us to better control the
environment for the tests.
2026-04-27 09:25:51 -07:00
Mitchell Hashimoto
1ed22a5210 renderer: fix preedit range width (#12479)
Related to #12466

`Preedit.range()` returns an inclusive range, but the end position was
calculated as `start + w`. For wide preedit text, this covers one extra
cell.

In Debug builds, Korean IME composition between existing Hangul
characters can panic with:
`index out of bounds: index 2, len 2`

I reproduced this reliably when there are two Hangul characters to the
right of the cursor. For example, type `가나다`, move the cursor between
`가` and `나`, then start a new Korean IME composition. With the old range
calculation, the renderer skips the first wide character plus the head
cell of the next wide character, then resumes on that character's spacer
tail.

This changes the inclusive end to `start + (w - 1)` and adds focused
tests for narrow, wide, and right-edge preedit ranges.

This does not fully fix the visual behavior reported in #12466. The
adjacent character can still disappear during composition, so this PR
only fixes the crash side of the problem.
2026-04-27 09:24:55 -07:00
Mitchell Hashimoto
c19ce03b3e fix: update Se terminfo entry to reset cursor to configured default (#12487)
## Problem

Current `Se` sequence (reset cursor style) is `\E[2 q`, which always
sets steady block, regardless of user config.

## Solution

Update sequence to `\E[0 q`, which sets the cursor style to the user
configured default cursor.

fix https://github.com/ghostty-org/ghostty/issues/12482
Helps with neovim issue: https://github.com/neovim/neovim/issues/38987

## AI Disclosure

I didn't use AI for this, haha. Unless you count random questions to
learn about terminfo beforehand, but I relied on [legit
resources](https://invisible-island.net/xterm/terminfo.html) for real
info. It says:

>  Se resets the cursor style to the terminal power-on default.

I think the useful interpretation is to set the user configured default.
2026-04-27 09:23:51 -07:00
Mitchell Hashimoto
5031973623 input: remove translated in capi (#12490)
Follow up for a3462dd2bd

<img width="884" height="255" alt="image"
src="https://github.com/user-attachments/assets/da4e4dd5-e645-40ed-8e9c-0ed8c9aee1c4"
/>
2026-04-27 09:23:25 -07:00
Lukas
971753074b input: remove translated in capi
Follow up for a3462dd2bd
2026-04-27 13:39:51 +02:00
Kyle Sower
6c68650920 fix: update Se terminfo entry to reset cursor to configured default 2026-04-26 22:31:01 -05:00
ghostty-vouch[bot]
8769d32202 Update VOUCHED list (#12485)
Triggered by [discussion
comment](https://github.com/ghostty-org/ghostty/discussions/12484#discussioncomment-16723661)
from @jcollie.

Vouch: @kylesower

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-04-27 03:16:59 +00:00
dobbylee
ac67a6160c renderer: fix preedit range width 2026-04-27 01:17:43 +09:00
Andrei Lebedev
12ac19939c feat: add middle-click action configuration 2026-04-27 01:27:22 +10:00
Lukas
df365baf18 test: add test plan and override default config when running tests 2026-04-26 14:49:20 +02:00
Lukas
a7eaecf929 test: always use temporary config when running ui tests 2026-04-26 13:53:36 +02:00
Lukas
b66258806e test: only change themes in theme tests 2026-04-26 13:53:36 +02:00
Mitchell Hashimoto
c74f6d56d1 os: use GetTempPathW for allocTmpDir on Windows (#12469)
`allocTmpDir` previously read `%TMP%` via `getenvW` and returned `null`
if the variable wasn't set, requiring each caller to to deal with the
nullable. Unfortunately, there isn't a platform-neutral default value
that makes sense for those cases (i.e. `/tmp` is POSIX-y).

We now use `GetTempPathW` on Windows, which is the official way to get
this directory: `TMP` → `TEMP` → `USERPROFILE` → `GetWindowsDirectoryW`.

With a real system call behind it, the function no longer needs to be
nullable: the only remaining failure modes are OOM (propagated) and the
syscall itself failing or returning data we can't decode. In those later
cases, we use `C:\Windows\Temp` as a fallback, similar to how we use
`/tmp` in the POSIX case.

The Windows path always allocates so it still must be paired with
`freeTmpDir`, which matches the existing contract.

---

*AI Disclosure:* I verified the Windows path using Claude and Zig's
cross-compilation capabilities because I don't have a Windows
environment in which to test this. I do fully understand the code based
on my prior life as a Windows game developer though.
2026-04-25 20:49:54 -07:00
Mitchell Hashimoto
278041c4bc flatpak: terminate session if Ghostty disconnects from bus (#12427)
This makes sure that if Ghostty crashes, commands spawned are also
terminated automatically by the Flatpak Session Helper.

The few crashes I got left a lot of background processes, some of them
pretty heavy and took awhile to be figured out.
2026-04-25 20:49:33 -07:00
Jon Parise
8b90efd913 os: use GetTempPathW for allocTmpDir on Windows
`allocTmpDir` previously read `%TMP%` via `getenvW` and returned `null`
if the variable wasn't set, requiring each caller to to deal with the
nullable. Unfortunately, there isn't a platform-neutral default value
that makes sense for those cases (i.e. `/tmp` is POSIX-y).

We now use `GetTempPathW` on Windows, which is the official way to get
this directory: `TMP` → `TEMP` → `USERPROFILE` → `GetWindowsDirectoryW`.

With a real system call behind it, the function no longer needs to be
nullable: the only remaining failure modes are OOM (propagated) and the
syscall itself failing or returning data we can't decode. In those later
cases, we use `C:\Windows\Temp` as a fallback, similar to how we use
`/tmp` in the POSIX case.

The Windows path always allocates so it still must be paired with
`freeTmpDir`, which matches the existing contract.
2026-04-25 21:44:37 -04:00
Leorize
8b8f7136d0 flatpak: don't assume c_uint to be u32
Thanks pluie for the pointer.
2026-04-25 17:04:26 -07:00
Mitchell Hashimoto
67b5783bdd os: RANDOM_BASENAME_LEN -> random_basename_len (#12467) 2026-04-25 13:34:28 -07:00
Jon Parise
13ada38ac4 os: RANDOM_BASENAME_LEN -> random_basename_len 2026-04-25 16:29:25 -04:00
Mitchell Hashimoto
e9ca0f8c9a core: Acquire renderer state mutex before calling processLinks (#12463)
Holding the renderer state mutex is a documented precondition of
`processLinks`, but `mouseButtonCallback` previously called the function
without the mutex.

This creates a race with the I/O thread's `processOutput`, which can
prune scrollback pages while `processLinks` is reading them, resulting
in a use-after-free segfault. See
https://github.com/ghostty-org/ghostty/discussions/12409 (Linux: crash
while selecting text).


57b5e1e250/src/Surface.zig (L4354-L4355)


57b5e1e250/src/Surface.zig (L3822-L3824)

995e4e375 (os: open) changed the body of `processLinks` to be
non-trivial and documented the precondition, but the lock was not held
at the call site.
2026-04-25 13:21:38 -07:00
Mitchell Hashimoto
0e0bcafed5 macOS: remove manual invalidateRestorableState() (#12464)
This should be safe to delete now after #12461.

I tested saving 27 tabs, 4 with 2 splits,
`TerminalRestorable.encode(with:` finished successfully.

And I check the breakpoints when the Sparkle sends
`-[NSRunningApplication treminate]`. The call stack at `-[NSResponder
invalidateRestorableState]` is pretty much the same as quitting via
`cmd+q`.
2026-04-25 13:16:08 -07:00
Mitchell Hashimoto
aedf39f3bd macOS: support migrations when restoring window state (#12461)
First two commits fix the issue when upgrading from 1.2.x to 1.3.x.
(#11304)

> To double check if this pr really fixes the issue, you can either
archive a release build, sign with the same profile, and override
manually.
> 
> Or you can find the `savedState` files (located in `~/Library/Daemon\
Containers/<uuid>`), can copy them the local build dir (which is what I
did), and run the debug build.

Following commits add tests for migrations and some logs.

**Currently the minimum version is set to 1.2.x**, since there's a lot
changes comparing to 1.1.x. It will be difficult to restore
`Ghostty.SplitNode` -> `SplitTree<Ghostty.SurfaceView>` without
introducing a lot of checks.
2026-04-25 13:15:50 -07:00
Mitchell Hashimoto
8e1dfbcf3e os: add randomTmpPath for allocating temp paths (#12465)
Factor TempDir's name generation into a reusable `randomBasename` (16
random bytes, url-safe base64) and add `randomTmpPath` on top, which
composes `allocTmpDir` + `randomBasename` into a single allocated path
in the form `{TMPDIR}/{prefix}{random}` (`mktemp(1)`-ish).

This is convenient for callers who want a unique path under TMPDIR (for
a temporary file, socket, etc.) without having to think about basename
buffer sizing or path joining.

Also, use `std.base64.url_safe_no_pad.Encoder` instead of the custom
base64 alphabet, which is exactly equivalent.
2026-04-25 13:14:30 -07:00
Jon Parise
c9d2285f63 os: add randomTmpPath for allocating temp paths
Factor TempDir's name generation into a reusable `randomBasename` (16
random bytes, url-safe base64) and add `randomTmpPath` on top, which
composes `allocTmpDir` + `randomBasename` into a single allocated path
in the form `{TMPDIR}/{prefix}{random}` (mktemp(1)-ish).

This is convenient for callers who want a unique path under TMPDIR (for
a temporary file, socket, etc.) without having to think about basename
buffer sizing or path joining.

Also, use `std.base64.url_safe_no_pad.Encoder` instead of the custom
base64 alphabet, which is exactly equivalent.
2026-04-25 15:52:40 -04:00
Lukas
385376185c macOS: remove manual invalidateRestorableState() 2026-04-25 21:25:38 +02:00
Lukas
231f6f4c75 macOS: move the restoration logs 2026-04-25 21:06:20 +02:00
Lukas
72c03e7fb8 macOS: add window restoration tests 2026-04-25 21:06:20 +02:00
Lukas
bfe07bb99e macOS: add InternalState to cover migrations 2026-04-25 20:25:17 +02:00
Lukas
5b89671d51 macOS: make terminal restorable state compatible with 1.2.3(v5) 2026-04-25 20:22:59 +02:00
Lukas
8ebf4f70e5 macOS: make tab color optional 2026-04-25 20:09:55 +02:00
Mitchell Hashimoto
b613ffcfd8 surface: respect semantic prompt boundaries for links (#12435)
Link detection currently expands the clicked location to a full line
before running the configured regexes. When semantic prompt markers are
present, this can cause prompt text and neighboring content to be
matched together even though they are distinct semantic regions.

Use semantic prompt boundaries when selecting the text to inspect for
link matching. This keeps prompt text separate from the content beside
it and avoids folding prompt text into double-click link/path selection.

Add a regression test that models a prompt and command on the same line
and verifies the prompt region and input region remain separate.

----

this is a fix for the issue users reported in
https://github.com/ghostty-org/ghostty/discussions/11415

**disclaimer**: I used codex addon within Cursor to research this
bug/regression and find a proper fix for it.
2026-04-25 10:32:15 -07:00
Vasyl Zuziak
c4a671ba5a fix: remove test as has been suggested in comment 2026-04-25 19:26:22 +02:00
Mitchell Hashimoto
c5c3cf16ba feat: add GTK keybinds (matching the idiomatic Linux convention used by Firefox, GNOME Terminal, and VSCode) for move_tab (#12458)
> Re-submitting #11857, which was auto-closed pre-vouch. I'm now in the
vouched list.

## Summary

Adds default keybinds for `move_tab` on GTK/Linux, matching the
idiomatic Linux convention used by Firefox, GNOME Terminal, and VSCode:

- **`Ctrl+Shift+PageUp`** → `move_tab:-1` (move tab left)
- **`Ctrl+Shift+PageDown`** → `move_tab:1` (move tab right)

To free these keys, `jump_to_prompt` is reassigned to `Ctrl+Shift+Arrow
Up/Down`, which:
- Mirrors the macOS default (`Cmd+Shift+Arrow Up/Down`)
- Is currently unbound on GTK
- Maintains directional consistency (up = previous, down = next)

The new `move_tab` bindings use `putFlags` with `performable: true` to
match the pattern used by other tab actions (`previous_tab`,
`next_tab`).

Closes #4998

## Changes
- `src/config/Config.zig`: Reassign `jump_to_prompt` from
`Ctrl+Shift+PageUp/PageDown` to `Ctrl+Shift+Arrow Up/Down` (GTK only)
- `src/config/Config.zig`: Add `move_tab` default keybinds as
`Ctrl+Shift+PageUp/PageDown` (GTK only)

## Test plan
- [ ] Build on Linux/GTK: `zig build`
- [ ] Verify `Ctrl+Shift+PageUp` moves current tab left
- [ ] Verify `Ctrl+Shift+PageDown` moves current tab right
- [ ] Verify `Ctrl+Shift+Arrow Up` jumps to previous prompt
- [ ] Verify `Ctrl+Shift+Arrow Down` jumps to next prompt
- [ ] Verify `Ctrl+PageUp/PageDown` still switches tabs (unchanged)
- [ ] Verify macOS keybinds are unaffected
2026-04-25 10:10:33 -07:00
ghostty-vouch[bot]
c9ba2b2afa Update VOUCHED list (#12457)
Triggered by [discussion
comment](https://github.com/ghostty-org/ghostty/discussions/11628#discussioncomment-16712930)
from @mitchellh.

Denounce: @4RH1T3CT0R7

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-04-25 16:54:04 +00:00
Enzo William
14c06312d5 feat: add default GTK keybinds for move_tab
Reassign jump_to_prompt from Ctrl+Shift+PageUp/PageDown to
Ctrl+Shift+Arrow Up/Down on GTK, freeing the idiomatic Linux
keybinds (Ctrl+Shift+PageUp/PageDown) for move_tab.

This matches the tab-moving keybinds used by Firefox, GNOME Terminal,
and VSCode. The new jump_to_prompt binding mirrors the macOS pattern
(Cmd+Shift+Arrow Up/Down).

Closes #4998

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-25 13:53:37 -03:00
ghostty-vouch[bot]
4d1bb9efe4 Update VOUCHED list (#12456)
Triggered by [discussion
comment](https://github.com/ghostty-org/ghostty/discussions/11685#discussioncomment-16712917)
from @mitchellh.

Denounce: @enkr1

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-04-25 16:52:41 +00:00
Mitchell Hashimoto
df4f981592 Fix Korean IME committed text handling for arrow keys (#12447)
Fixes #11461

- Send Korean IME committed text as a separate text-only key event when
arrow-key handling commits preedit text.
- Replay arrow navigation after the committed text is sent. Do not
replay plain Left Arrow to match Terminal.app behavior.
- Manually tested Arrow keys and Opt/Cmd+Arrow during Korean preedit on
macOS.
2026-04-25 09:47:37 -07:00
ghostty-vouch[bot]
03e08f0c89 Update VOUCHED list (#12455)
Triggered by [discussion
comment](https://github.com/ghostty-org/ghostty/discussions/11795#discussioncomment-16712869)
from @mitchellh.

Vouch: @bch

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-04-25 16:46:35 +00:00
ghostty-vouch[bot]
5a3b0c9c49 Update VOUCHED list (#12454)
Triggered by [discussion
comment](https://github.com/ghostty-org/ghostty/discussions/11858#discussioncomment-16712861)
from @mitchellh.

Vouch: @enzowilliam

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-04-25 16:45:31 +00:00
ghostty-vouch[bot]
e0c71dd41b Update VOUCHED list (#12453)
Triggered by [discussion
comment](https://github.com/ghostty-org/ghostty/discussions/11882#discussioncomment-16712859)
from @mitchellh.

Vouch: @mpatankar6

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-04-25 16:45:08 +00:00
ghostty-vouch[bot]
0e707ba3f6 Update VOUCHED list (#12452)
Triggered by [discussion
comment](https://github.com/ghostty-org/ghostty/discussions/11975#discussioncomment-16712831)
from @mitchellh.

Vouch: @rightaditya

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-04-25 16:43:11 +00:00
ghostty-vouch[bot]
51590ad7f1 Update VOUCHED list (#12451)
Triggered by [discussion
comment](https://github.com/ghostty-org/ghostty/discussions/11993#discussioncomment-16712826)
from @mitchellh.

Vouch: @VoidNV

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-04-25 16:42:24 +00:00
Mitchell Hashimoto
69452b5c6f Sync middle-click paste with copy-on-select (#12443)
Implements the behavior from #9788.

Today, middle-click paste always reads from the selection clipboard (or
the
system clipboard on platforms without a selection clipboard). With this
change
it follows `copy-on-select`:

- `true`: selection clipboard (unchanged)
- `clipboard`: system clipboard
- `false`: selection clipboard (also unchanged, preserves traditional
X11
  middle-click behavior)

The idea is symmetry: if `copy-on-select = clipboard` writes selections
to the
system clipboard, middle-click should come back from there too.

Also updated the config doc comment, which previously claimed
middle-click
"will always use the selection clipboard".

### Testing

`zig build test` passes locally (macOS, Zig 0.15.2).

Built and runtime-tested via the fork's CI:
https://github.com/007hacky007/ghostty/actions/runs/24707475544 - I'm
running the resulting binary daily and the three `copy-on-select` modes
behave as described above.
2026-04-25 09:39:12 -07:00
Mitchell Hashimoto
2dee1f1a3f Update VOUCHED list (#12445)
Triggered by [discussion
comment](https://github.com/ghostty-org/ghostty/discussions/12263#discussioncomment-16712767)
from @mitchellh.

Vouch: @thoutbeckers
2026-04-25 09:36:41 -07:00
ghostty-vouch[bot]
ee81a6e1c6 Update VOUCHED list (#12450)
Triggered by [discussion
comment](https://github.com/ghostty-org/ghostty/discussions/12108#discussioncomment-16712799)
from @mitchellh.

Vouch: @lebdron

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-04-25 16:36:12 +00:00
ghostty-vouch[bot]
4c046efbb1 Update VOUCHED list (#12449)
Triggered by [discussion
comment](https://github.com/ghostty-org/ghostty/discussions/12141#discussioncomment-16712795)
from @mitchellh.

Vouch: @AkimioJR

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-04-25 16:35:39 +00:00
ghostty-vouch[bot]
e0d0fbe0ad Update VOUCHED list (#12448)
Triggered by [discussion
comment](https://github.com/ghostty-org/ghostty/discussions/12169#discussioncomment-16712790)
from @mitchellh.

Vouch: @knu

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-04-25 16:34:38 +00:00
ghostty-vouch[bot]
8c3db43c86 Update VOUCHED list (#12446)
Triggered by [discussion
comment](https://github.com/ghostty-org/ghostty/discussions/12272#discussioncomment-16712769)
from @mitchellh.

Vouch: @dkinzler

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-04-25 16:31:04 +00:00