fix: C action buffer lengths, byte-faithful callbacks, runtime finalize

Resolves the 8 gaps in docs/rewrite/notes/review-rv10-codex.md
(final-Z13.md):
- P1 memory safety: the C key-table action (and hovered-link/open-URL
  payloads) could advertise a length larger than the NUL-truncated
  allocation. A counted_bytes helper now passes the full bytes plus a
  trailing NUL with len == byte count, like Zig's backing slices; all
  C pointer+len structs were audited and tested to read exactly len.
- ghostty-bench args follow cli/args.zig (ordered duplicates, empty
  value resets, first bad value errors, parseBool spellings); the C
  benchmark tokenizer uses the ArgIteratorGeneral port.
- OSC 7 derived titles, desktop notifications and OSC 8 hover links
  forward raw bytes to the runtime; GTK converts only for display.
- C diagnostic getter exports original bytes; escaped surrogates report
  Utf8CannotEncodeSurrogateHalf; config float formatters print `nan`.
- Config::finalize_for(AppRuntime): GTK-only finalization (gtk-single-
  instance=detect) and termio's cgroup hooks apply only to the GTK
  runtime, so libghostty keeps `detect` like Zig.

Test Plan:
- cargo test --workspace --no-fail-fast (exit 0)
- cargo test -p ghostty-lib --features test-platform
- just check-wasm (macOS/Windows clippy re-run after the concurrent
  terminal changes land)
This commit is contained in:
ddidderr committed 2026-10-12 13:09:04 +02:00
1 parent 95fe02b55a
commit f2ccb04aa4
21 files changed
+479 -133

No files matched your search

+2 -2
View File
@@ -302,7 +302,7 @@ fn osc8_link_hover_and_ctrl_click_opens() {
// hover with ctrl over "here" (cols 3..6)
h.move_to(4, 0, Some(ctrl()));
let url = h.wait_action("mouse over link", |a| match a {
Action::MouseOverLink(l) if !l.url.is_empty() => Some(l.url.clone()),
Action::MouseOverLink(l) if !l.url.is_empty() => Some(l.url.to_text_lossy()),
_ => None,
});
assert_eq!(url, "http://osc8.example/x");
@@ -324,7 +324,7 @@ fn regex_url_ctrl_click_opens_and_plain_click_does_not() {
h.move_to(30, 2, Some(none()));
h.move_to(20, 0, Some(ctrl()));
h.wait_action("hover", |a| match a {
Action::MouseOverLink(l) if l.url.contains("ghostty.org") => Some(()),
Action::MouseOverLink(l) if l.url.to_text_lossy().contains("ghostty.org") => Some(()),
_ => None,
});
h.click(20, 0, L, ctrl());
+15 -2
View File
@@ -229,11 +229,24 @@ fn osc52_read_allow_and_deny() {
assert!(h.rt.confirmations.borrow().is_empty());
}
#[test]
fn desktop_notification_keeps_invalid_utf8_bytes() {
let h = harness!(Harness::recorder(r"\033]777;notify;T\377;B\377\007", &[]));
let (t, b) = h.wait_action("osc777", |a| match a {
Action::DesktopNotification(n) => Some((n.title.to_bytes(), n.body.to_bytes())),
_ => None,
});
assert_eq!(
(t.as_slice(), b.as_slice()),
(b"T\xff".as_slice(), b"B\xff".as_slice())
);
}
#[test]
fn desktop_notifications() {
let h = harness!(Harness::recorder(r"\033]9;just a body\007", &[]));
let (t, b) = h.wait_action("osc9", |a| match a {
Action::DesktopNotification(n) => Some((n.title.clone(), n.body.clone())),
Action::DesktopNotification(n) => Some((n.title.to_text_lossy(), n.body.to_text_lossy())),
_ => None,
});
assert_eq!(b, "just a body");
@@ -244,7 +257,7 @@ fn desktop_notifications() {
&[]
));
let (t, b) = h.wait_action("osc777", |a| match a {
Action::DesktopNotification(n) => Some((n.title.clone(), n.body.clone())),
Action::DesktopNotification(n) => Some((n.title.to_text_lossy(), n.body.to_text_lossy())),
_ => None,
});
assert_eq!((t.as_str(), b.as_str()), ("My Title", "My Body"));