This is an extension agreed upon by modern terminals to indicate that
they support copying to the clipboard with XTerm's OSC 52 sequence. It
is only reported when writing to the clipboard is actually allowed.
## Description
Yet another edge case in #2484
When macOS's "Private WiFi address" feature is enabled it'll change the
hostname to a mac address. Mac addresses look like URIs with a hostname
and port component, e.g. `12:34:56:78:90:12` where `:12` looks like port
`12`. However, mac addresses use hex numbers and as such can also
contain letters `a` through `f`. So, a mac address like
`ab💿ef🆎cd:ef` is valid, but will not be parsed as a URI, because
`:ef` is not a valid port.
This commit attempts to fix that by checking if the hostname is a valid
mac address when `std.Uri.parse()` fails and constructing a new
`std.Uri` struct using that information.
It's not perfect, but is equally compliant with the URI spec as
`std.Uri` currently is. Meaning not at all compliant 😅
## Testing instructions
### Unit tests
> [!IMPORTANT]
> I don't know if these tests are run in CI or if they're picked up by
`zig build test`. I get an unrelated crash that mentions `minidump` and
an invalid OSC command when I try to run `zig build test` on my mac.
1. Make sure `zig test src/os/hostname.zig` is passing.
### Manual testing instructions
#### Setup - Enable the "Private WiFi address" setting
> [!IMPORTANT]
> You must be connected to WiFi to be able to test this.
1. Open your mac's "System Settings".
2. Go to Network → Wi-Fi → Details.
<img width="710" alt="image"
src="https://github.com/user-attachments/assets/fe30cfe7-8e77-4421-8b36-2f7aab0918dd"
/>
3. Set the "Private Wi-Fi address" setting to `Rotating`.
<img width="710" alt="image"
src="https://github.com/user-attachments/assets/bd695c20-106c-46bd-8862-cbdce55fed6f"
/>
> [!IMPORTANT]
> Now you wait. The private Wi-Fi address will eventually rotate to a
mac address that ends with a non-digit, e.g. `0a`, `ff`, `e2`, etc.
You'll notice this when your shell integration stops working, e.g. you
open a new tab in Ghostty and the shell is in your home directory
instead of whichever directory you had open in your previous tab.
#### Testing the changes
1. Open Ghostty.
3. `cd` to any directory that isn't the default (usually `$HOME`)
directory, e.g. `cd Documents`.
4. Open a new tab (<kbd>Cmd+T</kbd>) or split (<kbd>Cmd+D</kbd>).
5. Assuming the setup steps have been followed you should:
* On `main`: land in `$HOME` in the new tab or split.
* On this branch: land in the same working directory as the original tab
or split.
When macOS's "Private WiFi address" feature is enabled it'll change the
hostname to a mac address. Mac addresses look like URIs with a hostname
and port component, e.g. 12:34:56:78:90:12 where `:12` looks like port
12. However, mac addresses can also contain letters a through f, so a
valid mac address like ab💿ef🆎cd:ef is valid, but will not be parsed
as a URI, because `:ef` is not a valid port.
This commit attempts to fix that by checking if the hostname is a valid
mac address when `std.Uri.parse()` fails and constructing a new std.Uri
struct using that information.
It's not perfect, but is equally compliant with the URI spec as std.Uri
currently is.
This brings the behavior of mode 47, 1047, and 1049 much closer to
xterm's behavior. I found that our prior implementation had many
deficiencies.
For example, we weren't properly copying the cursor state back to the
primary screen from the alternate screen for modes 47 and 1047. And we
weren't saving/restoring cursor state unconditionally for mode 1049 even
if we were already in the alternate screen.
These are weird, edgy behaviors that I don't think anyone expected
(evidence by there being no bug reports about them), but they are bugs
nontheless.
Many tests added.
Related to #3224
Previously, Ghostty used a static API for async event handling: io_uring
on Linux, kqueue on macOS. This commit changes the backend to be dynamic
on Linux so that epoll will be used if io_uring isn't available, or if
the user explicitly chooses it.
This introduces a new config `async-backend` (default "auto") which can
be set by the user to change the async backend in use. This is a
best-effort setting: if the user requests io_uring but it isn't
available, Ghostty will fall back to something that is and that choice
is up to us.
Basic benchmarking both in libxev and Ghostty (vtebench) show no
noticeable performance differences introducing the dynamic API, nor
choosing epoll over io_uring.
The Ghostty implementation of OSC 21 (Kitty color protocol) currently
responds to *all* OSC 21 sequences. It should not respond to a set, nor
a reset command. Fix the implementation so that we only respond if a
query was received.
The renderer must track if the foreground, background, and cursor colors
are explicitly set by an OSC so that changes are not overridden when the
config file is reloaded.
Make the foreground_color and background_color fields in the Terminal
struct optional values so that we can determine if a foreground or
background color was explicitly set with an OSC 10 or OSC 11 sequence.
This makes the logic a bit simpler to reason about (i.e.
`foreground_color` is now always "the color set by an OSC 10 sequence"
while `default_foreground_color` is always "the color set by the config
file") and also fixes an issue where an OSC 10 or OSC 11 query would not
report the correct color after a config update changed the foreground or
background color.
The `cursor_color` field was already optional, with the same semantics
(it is only non-null when explicitly set with an OSC 12) so this brings
all three of these fields into alignment.
Related #2755
From the mode 2031 spec[1]:
> Send CSI ? 2031 h to the terminal to enable unsolicited DSR (device status
> report) messages for color palette updates and CSI ? 2031 l respectively to
> disable it again.
>
> The sent out DSR looks equivalent to the already above mentioned. This
> notification is not just sent when dark/light mode has been changed by the
> operating system / desktop, but also if the user explicitly changed color
> scheme, e.g. by configuration.
My reading of this paired with the original discussion is that this is
meant to be sent out for anything that could possibly change terminal
colors.
Previous to this commit, we only sent out the DSR when the actual system
light/dark mode changed. This commit changes it to send out the DSR on
any operation that _may_ change the terminal colors.
[1]: https://contour-terminal.org/vt-extensions/color-palette-update-notifications/#example-source-code
When an empty string is given to OSC7, the pwd is reset to nil (as if
the terminal never received a pwd report to begin with). This is
analogous to how OSC0/2 reset the title to nil when given an empty
string.
This is practically useful for macOS because it allows our proxy icon to
also be reset instead of being stuck on the last known path.
This breaks from any known terminal behavior. As far as I can find, this
is totally unspecified so we're somewhat free to do what we want. I
don't think any terminal programs depend on this behavior, so I think
it's safe to change it.
Fixes#2651
First, our OSC parser didn't allow blank OSC 0 or 2 requests. This
should be allowed and this fixes that with a test.
Second, it seems many terminals (iTerm2, Kitty) treat setting a blank
title as resetting to whatever the default title is rather than
explicitly setting it as blank. If a program wants a title to be blank
they should send a single space. This commit follows this behavior.
The explanation now refers to RFC 793 instead of just claiming some
arbitrary value as truth. The previous value was correct, but now there
is a proper source for the correct value.
When the focus reporting mode (1004) is enabled, send the current focus
state. This allows applications to track their own focus state without
first having to wait for a focus event (or query
it by sending a DECSET followed by a DECRST).
Ghostty's focus state is stored only in the renderer, where the termio
thread cannot access it. We duplicate the focus state tracking in the
Terminal struct with the addition of a new (1-bit) flag. We duplicate
the state because the renderer uses the focus state for its own purposes
(in particular, the Metal renderer uses the focus state to manage
its DisplayLink), and synchronizing access to the shared terminal state
is more cumbersome than simply tracking the focus state in the renderer
in addition to the terminal.
- Cap the total number of requests at twice the maximum number of
keys (currently 263, so 526 requests). Basically you can set and then
query every key in one message. This is an absurdly high number
but should prevent serious DOS attacks.
- Clarify meaning of new hex color codes.
- Better handle sending messages to the renderer in a way that should
prevent deadlocks.
- Handle 0-255 palette color requests by creatively using non-exhautive
enums.
- Fix an error in the query reply.