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:
1 parent
7b3fffded3
commit
da9599140b
21 files changed
+479
-133
No files matched your search
@@ -1066,6 +1066,7 @@ impl Subprocess {
|
||||
let pre_info = self.rt_pre_exec_info;
|
||||
let post_info = self.rt_post_fork_info;
|
||||
let create_scope = self.create_scope.clone();
|
||||
let gtk = ghostty_config::config::app_runtime() == ghostty_config::config::AppRuntime::Gtk;
|
||||
let mut cmd = Command {
|
||||
path: self.args[0].clone(),
|
||||
args: self.args.clone(),
|
||||
@@ -1081,15 +1082,19 @@ impl Subprocess {
|
||||
let _ = pty::child_pre_exec(master_raw, slave_raw);
|
||||
None
|
||||
})),
|
||||
rt_pre_exec: Some(Box::new(move || linux_cgroup_pre_exec(&pre_info))),
|
||||
rt_post_fork: Some(Box::new(move |c: &Command| {
|
||||
let create = |pid, mem, tasks| match &create_scope {
|
||||
Some(f) => f(pid, mem, tasks),
|
||||
None => None,
|
||||
};
|
||||
linux_cgroup_post_fork(c, &post_info, create)
|
||||
.map_err(|_: PostForkError| PostForkError)
|
||||
})),
|
||||
// Zig installs these only when the apprt declares them
|
||||
// (`@hasDecl(apprt.runtime, "pre_exec")`): GTK.
|
||||
rt_pre_exec: gtk.then(|| Box::new(move || linux_cgroup_pre_exec(&pre_info)) as Box<_>),
|
||||
rt_post_fork: gtk.then(|| {
|
||||
Box::new(move |c: &Command| {
|
||||
let create = |pid, mem, tasks| match &create_scope {
|
||||
Some(f) => f(pid, mem, tasks),
|
||||
None => None,
|
||||
};
|
||||
linux_cgroup_post_fork(c, &post_info, create)
|
||||
.map_err(|_: PostForkError| PostForkError)
|
||||
}) as Box<_>
|
||||
}),
|
||||
pid: None,
|
||||
};
|
||||
|
||||
|
||||
@@ -839,9 +839,8 @@ impl StreamHandler<'_> {
|
||||
//-------------------------------------------------------------------------
|
||||
// OSC
|
||||
|
||||
fn window_title(&mut self, title: &str) {
|
||||
fn window_title(&mut self, title: &[u8]) {
|
||||
let mut buf = [0u8; SET_TITLE_LEN];
|
||||
let title = title.as_bytes();
|
||||
if title.len() >= buf.len() {
|
||||
log::warn!("change title requested larger than our buffer size, ignoring");
|
||||
return;
|
||||
@@ -949,7 +948,7 @@ impl StreamHandler<'_> {
|
||||
// If we haven't seen a title, we're using the pwd as our title.
|
||||
// Set it to blank which will reset our title behavior.
|
||||
if !self.st.seen_title {
|
||||
self.window_title("");
|
||||
self.window_title(b"");
|
||||
debug_assert!(!self.st.seen_title);
|
||||
}
|
||||
|
||||
@@ -1015,8 +1014,7 @@ impl StreamHandler<'_> {
|
||||
|
||||
// If we haven't seen a title, use our pwd as the title.
|
||||
if !self.st.seen_title {
|
||||
// Titles are text; only the title is lossy.
|
||||
self.window_title(&String::from_utf8_lossy(&path));
|
||||
self.window_title(&path);
|
||||
self.st.seen_title = false;
|
||||
}
|
||||
Ok(())
|
||||
@@ -1501,7 +1499,7 @@ impl stream::Handler for StreamHandler<'_> {
|
||||
Action::EndHyperlink => self.terminal.screen_mut().end_hyperlink(),
|
||||
Action::ActiveStatusDisplay(v) => self.terminal.status_display = v,
|
||||
Action::Decaln => self.terminal.decaln()?,
|
||||
Action::WindowTitle(v) => self.window_title(v.title),
|
||||
Action::WindowTitle(v) => self.window_title(v.title.as_bytes()),
|
||||
Action::ReportPwd(v) => self.report_pwd(v.url)?,
|
||||
Action::ShowDesktopNotification(v) => self.show_desktop_notification(v.title, v.body),
|
||||
Action::ProgressReport(v) => self.progress_report(v),
|
||||
@@ -1984,6 +1982,17 @@ mod tests {
|
||||
assert!(!h.stream.state.seen_title);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn report_pwd_title_keeps_raw_bytes() {
|
||||
let mut h = Harness::new();
|
||||
h.feed(b"\x1b]7;file://localhost/tmp/%FF\x1b\\");
|
||||
let msgs = h.surface_msgs();
|
||||
assert!(
|
||||
matches!(msgs.last(), Some(SurfaceMessage::SetTitle(t)) if t.starts_with(b"/tmp/\xff\0")),
|
||||
"{msgs:?}"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn report_pwd_remote_host_ignored_and_reset() {
|
||||
let mut h = Harness::new();
|
||||
|
||||
Reference in new issue
Block a user