When ghostty is used as a Zig dependency, detect this by comparing the
build root with ghostty's source directory. Skip git detection entirely
and use the version from build.zig.zon.
This fixes build failures when downstream projects have git tags that
don't match ghostty's version format. Previously, ghostty would read the
downstream project's git tags and panic at Config.zig:246 with:
\`\`\`
tagged releases must be in vX.Y.Z format matching build.zig
\`\`\`
**Reproduction:**
1. Create a project that uses ghostty as a Zig dependency
2. Tag the project with a version like \`v0.2.0\`
3. Run \`zig build\` → panic
**Fix:**
Use \`@src().file\` to get ghostty's source directory and compare it
with \`b.build_root\`. When they differ, ghostty is a dependency and we
skip git detection.
Thread:
https://ampcode.com/threads/T-197e6c33-b8f8-4b23-8fc8-7f6b6edd9f35
Detect if ghostty is being built as a dependency by comparing the build
root with ghostty's source directory. When used as a dependency, skip
git detection entirely and use the version from build.zig.zon.
This fixes build failures when downstream projects have git tags that
don't match ghostty's version format. Previously, ghostty would read
the downstream project's git tags and panic at Config.zig:246 with
"tagged releases must be in vX.Y.Z format matching build.zig".
Related to #1935
This adds a new structure `terminal.tmux.Viewer` which continues
building on all the prior tmux control mode work to add a full
bidirectional reconciliation loop to discover and sync terminal states
from tmux to Ghostty and vice versa. **This is the core, cross-platform
business logic that will power the GUIs, later.**
Our prior work were protocol building blocks, and this PR is an actual
functional piece of work. You can now start Ghostty, run `tmux -CC
attach`, and we _will_ be creating full blown terminals internal that
capture the content and mirror the state exactly (barring inevitable
bugs in something this complex). But, we don't yet show them visually.
:) And we don't yet send inputs to it (it's a viewer only, for now).
**This sucked.** The control mode protocol is difficult, to put it
mildly, for a variety of reasons. Correctness of this is going to be
hard. Therefore, I focused really hard on this design to make it **fully
unit test friendly.** We're able to simulate full tmux sessions and runt
through our state machine and assert various states. I think this will
be critical to correctness as we eventually collect real world data.
> [!WARNING]
>
> This does actually have user-impacting changes! When you run `tmux -CC
attach` we will now run our full control mode client. This could result
in bugs or crashes or other problems. This only activates if you have a
real tmux session, though, so it should be avoidable by most users.
Since we don't actually take our state and send it to the GUI or
anything, this should be pretty safe.
**AI disclosure:** I used AI for a lot of the protocol reverse
engineering and documentation to figure out how it all works. I designed
the architecture myself and implemented most of it manually.
Hello! I did read `CONTRIBUTING.md`. I understand that the preference is
to receive pull requests for existing open issues and acknowledge that
I'm skipping a step wrt the preferred workflow since there aren't any
open issues yet for #8419. Living dangerously and hoping for the best
here. I hope you'll see that I'm acting in good faith 🤞.
In my attempts to debug why `quick-terminal-size` config wasn't working
reliably for me, I discovered that the
[`ghostty_config_quick_terminal_size_s`](08c9661683/include/ghostty.h (L460-L480))
struct used to initialize `QuickTerminalSize` in Swift-land, more often
than not, didn't match what I had in my config file (e.g.
`quick-terminal-size = 75%, 50%`)
<details>
08c9661683/include/ghostty.h (L460-L480)08c9661683/macos/Sources/Ghostty/Ghostty.Config.swift (L507-L510)
</details>
Almost all mismatches seemed to be downstream of `tag` (for `primary`
and/or `secondary` `Size`) being an unexpected value for the
`ghostty_quick_terminal_size_tag_e` enum type, which led runtime
execution to fall into the `default` branch in this `switch` control
flow.
08c9661683/macos/Sources/Features/QuickTerminal/QuickTerminalSize.swift (L27-L38)
Looking at `src/config/CApi.zig`, `src/config/c_get.zig`,
`src/config/Config.zig`, and [Zig documentation for `extern
enum`](https://ziglang.org/documentation/master/#extern-enum), led me to
conclude that the crux of the issue is lack of guaranteed C ABI
compatibility for `Tag`
08c9661683/src/config/Config.zig (L7986-L7990)08c9661683/include/ghostty.h (L461-L465)
Further research revealed that based on C language spec alone, one
cannot assume a fixed width for `enum` and that the behaviour is
compiler dependant. But given how the Zig documentation suggests using
`enum(c_int)` for C-ABI-compatible enums and extern enums + comments
elsewhere in `Config.zig` suggesting that `enum(c_int)` was chosen for
extern compatibility,
08c9661683/src/config/Config.zig (L4877)
I think that this fix (changing `enum(u8)` to `enum(c_int)`) is likely
the most straightforward and appropriate one.
I am truly not sure why the tests never caught this, but I just fell for
the oldest trick in the book
Fixes a crash on GTK when a file is dragged then dropped into the
terminal - should not impact 1.2 as this is a casualty of the Zig 0.15
port
This fixes an issue I have on both macOS and Linux (ARM and x86_64)
where stack traces are broken for inlined functions. They don't point to
the proper location in the source code, making debugging difficult.
Release builds use the same previous function.
This was found by LLM hunting! We were not holding the lock properly
during these operations. There aren't any known cases where we can
directly attribute these races to issues but we did find at least one
consistent crash for a user when `linkAtPos` wasn't properly locked (in
another PR).
This continues those fixes.
From #9812
I'm not sure if this is the root cause of the crash in #9812 but the
LLM-discovered issue that we are not holding a lock here appears to be a
real issue. I manually traced the code paths and thought about this and
looked where we call `mouseRefreshLinks` in other places and this
appears to be a real bug.
Continuing just build foundation for #1935. There are no user-facing
changes here.
- This adds more command parsing for control mode output. I _believe_ we
now parse enough to at least render everything initially for most tmux
sessions. At least, from the VT parsing side (we haven't done any GUI
work whatsoever to react to this).
- This also adds a new layout string parser, e.g.
`159x48,0,0{79x48,0,0,79x48,80,0}` into a useful struct. We'll need this
to convert our visible layouts into actual GUI components, eventually.
- This adds a new output format parser for commands such as
`list-windows`. Control mode sends output framed in `%begin/%end`
blocks, but the output is in an arbitrary format depending on the input
command and unrelated to control mode itself. The output format parser
will let us parse this output.
I think this is a good place to stop and PR. I think next up I might try
hooking up a state machine to `src/termio/stream_handler.zig` to start
actually working with this. Before that, it may behoove me to change
`stream_readonly` to support non-readonly operations via callback so I
can do DCS and maybe unit test this whole thing too.... we'll see where
the wind blows.
**AI disclosure:** AI wrote many tests and did a lot of implementation
here. I reviewed everything manually though and understand it all
completely.
If the BEL character is received too frequently, the GUI thread can be
starved and Ghostty will lock up and eventually crash. This PR limits
BEL handling to 1 per 100ms.
Fixes#9800.