fix: close remaining gaps found by the completeness audit

Addresses the partial ports listed in
docs/rewrite/notes/review-completeness-codex.md (details in
docs/rewrite/notes/gaps-X2.md):
- Flatpak: the exit watcher no longer consumes the host command; a
  shared FlatpakSignalHandle lets closing a surface still SIGHUP the
  host process group.
- Child environment is byte-preserving end to end (ghostty_os::EnvMap
  of OsStrings) and Snap paths are filtered like filterSnapPaths.
- argv handling no longer panics on non-UTF-8 arguments.
- URI percent-decoding keeps raw bytes; OSC 7 pwd is passed through
  without lossy conversion.
- Keybinding comparison/hashing uses full Unicode case folding from a
  table generated from CaseFolding.txt (Unicode 17.0.0).
- Link regexes set Oniguruma's whole-search retry limit through the new
  minimal ghostty-onig crate (one SAFETY-commented FFI call).
- OSC 9 NUL-capacity check ordering matches Zig.
- Wayland registry binds/clears globals advertised after startup.
- Custom shaders may pass sampler2D to helper functions (naga adapter).
- +list-themes preview renders OSC 8 links, styled underlines and
  follows color-scheme reports.

Also includes the inspector's integration edits in shared files
(renderer state hook, termio VT recording, core/apprt-gtk wiring), X1's
config/input/cli additions for completion generation, and a fix for a
race in the termio backpressure test (it wrote before `stty raw`
applied, so the cooked line discipline dropped bytes past MAX_CANON).

Test Plan:
- cargo test --workspace
- cargo clippy --workspace --all-targets -- -D warnings
This commit is contained in:
ddidderr committed 2026-10-12 04:39:24 +02:00
1 parent 90ad602969
commit f4c020101c
61 files changed
+4210 -298

No files matched your search

+61 -3
View File
@@ -14,6 +14,10 @@
//! text hello world # IM commit of the rest of the line
//! wait 200 # milliseconds
//! screenshot /abs/out.png # dump the renderer's last frame (PNG)
//! inspector-screenshot /abs/out.png # dump the next frame of the terminal
//! # inspector window (PNG)
//! inspector-click 100 20 # left click in the inspector (logical px)
//! inspector-scroll 100 200 -3 # mouse wheel in the inspector
//! quit # quit the application
//! ```
//!
@@ -34,12 +38,15 @@ use crate::class::surface::Surface;
use crate::class::window::Window;
/// One parsed command.
#[derive(Debug, Clone, PartialEq, Eq)]
#[derive(Debug, Clone, PartialEq)]
pub enum Step {
Key(String),
Text(String),
Wait(u64),
Screenshot(String),
InspectorScreenshot(String),
InspectorClick(f64, f64),
InspectorScroll(f64, f64, f64),
Quit,
}
@@ -59,6 +66,31 @@ pub fn parse(script: &str) -> Result<Vec<Step>, String> {
"text" if !rest.is_empty() => Step::Text(rest.to_owned()),
"wait" => Step::Wait(rest.parse().map_err(|_| err("wait needs milliseconds"))?),
"screenshot" if !rest.is_empty() => Step::Screenshot(rest.to_owned()),
"inspector-screenshot" if !rest.is_empty() => {
Step::InspectorScreenshot(rest.to_owned())
}
"inspector-click" => {
let v: Vec<f64> = rest
.split_whitespace()
.map(str::parse)
.collect::<Result<_, _>>()
.map_err(|_| err("inspector-click needs x y"))?;
let [x, y] = v[..] else {
return Err(err("inspector-click needs x y"));
};
Step::InspectorClick(x, y)
}
"inspector-scroll" => {
let v: Vec<f64> = rest
.split_whitespace()
.map(str::parse)
.collect::<Result<_, _>>()
.map_err(|_| err("inspector-scroll needs x y dy"))?;
let [x, y, dy] = v[..] else {
return Err(err("inspector-scroll needs x y dy"));
};
Step::InspectorScroll(x, y, dy)
}
"quit" => Step::Quit,
_ => return Err(err(&format!("bad command {line:?}"))),
});
@@ -190,6 +222,30 @@ impl Runner {
Some(s) => s.debug_screenshot(&path),
None => log::warn!("debug input: no active surface"),
},
Step::InspectorClick(x, y) => match self.active_surface() {
Some(s) => {
if !s.debug_inspector_click(x, y) {
log::warn!("debug input: the surface has no inspector");
}
}
None => log::warn!("debug input: no active surface"),
},
Step::InspectorScroll(x, y, dy) => match self.active_surface() {
Some(s) => {
if !s.debug_inspector_scroll(x, y, dy) {
log::warn!("debug input: the surface has no inspector");
}
}
None => log::warn!("debug input: no active surface"),
},
Step::InspectorScreenshot(path) => match self.active_surface() {
Some(s) => {
if !s.debug_inspector_screenshot(&path) {
log::warn!("debug input: the surface has no inspector");
}
}
None => log::warn!("debug input: no active surface"),
},
}
}
}
@@ -228,8 +284,7 @@ mod tests {
#[test]
fn parse_script() {
let s =
"# c\n\nkey ctrl+shift+t\ntext hello world\nwait 200\nscreenshot /tmp/a.png\nquit\n";
let s = "# c\n\nkey ctrl+shift+t\ntext hello world\nwait 200\nscreenshot /tmp/a.png\ninspector-screenshot /tmp/i.png\ninspector-click 10 20\ninspector-scroll 1 2 -3\nquit\n";
assert_eq!(
parse(s).expect("valid script"),
vec![
@@ -237,6 +292,9 @@ mod tests {
Step::Text("hello world".into()),
Step::Wait(200),
Step::Screenshot("/tmp/a.png".into()),
Step::InspectorScreenshot("/tmp/i.png".into()),
Step::InspectorClick(10.0, 20.0),
Step::InspectorScroll(1.0, 2.0, -3.0),
Step::Quit
]
);