From dc4725e626b1194e1f68540c7c9ece552cfd108c Mon Sep 17 00:00:00 2001 From: roboomp Date: Wed, 5 Aug 2026 10:45:57 +0000 Subject: [PATCH] fix(natives): gate wayland capture capability on pipewire feature WaylandBackend::capabilities() hardcoded capture:true, but the PipeWire screencast path is compiled only under the wayland-pipewire feature, which is off by default and excluded from shipped Bazel addons (crate_features=[]). Released builds therefore advertised capture the binary could never do: every capture() call returned CaptureFailed, and callers trusting capabilities() retried into a guaranteed failure. Gate the capture flag and capture_permission on cfg!(feature = "wayland-pipewire") so the report matches the compiled-in path, and align docs/computer-use.md with what shipped builds actually support. Fixes #7700 --- .../src/desktop/linux/wayland/mod.rs | 33 +++++++++++++++++-- docs/computer-use.md | 4 +-- packages/natives/CHANGELOG.md | 4 +++ 3 files changed, 37 insertions(+), 4 deletions(-) diff --git a/crates/pi-natives/src/desktop/linux/wayland/mod.rs b/crates/pi-natives/src/desktop/linux/wayland/mod.rs index b949166fb..96b4a29d6 100644 --- a/crates/pi-natives/src/desktop/linux/wayland/mod.rs +++ b/crates/pi-natives/src/desktop/linux/wayland/mod.rs @@ -106,12 +106,19 @@ impl Backend for WaylandBackend { DesktopCapabilities { backend: "wayland".to_string(), display_server: Some("wayland".to_string()), - capture: true, + // The PipeWire screencast path is compiled in only under the + // wayland-pipewire feature; without it capture() hard-errors, so the + // capability report must not advertise a capture the binary cannot do. + capture: cfg!(feature = "wayland-pipewire"), input: self.input.is_some(), ax: self.ax.is_some(), background_window_input: false, delivery_modes: vec!["foreground".to_string(), "background".to_string()], - capture_permission: "prompt-or-granted".to_string(), + capture_permission: if cfg!(feature = "wayland-pipewire") { + "prompt-or-granted".to_string() + } else { + "unavailable".to_string() + }, input_permission: if self.input.is_some() { "granted".to_string() } else { @@ -263,4 +270,26 @@ mod tests { assert_eq!(err.code.as_str(), "BackgroundUnavailable"); assert!(err.message.contains("wayland-compositor-focus-only")); } + + #[test] + #[cfg(not(feature = "wayland-pipewire"))] + fn capabilities_report_no_capture_without_pipewire_feature() { + let mut backend = WaylandBackend { + display: DisplaySelector::All, + ax: None, + ax_error: None, + input: None, + input_error: None, + displays: Vec::new(), + }; + let caps = backend.capabilities(); + // Shipped builds compile without wayland-pipewire, so the capture path is + // absent; capabilities() must not advertise capture the binary cannot do. + assert!(!caps.capture, "capture must be false when the pipewire feature is off"); + assert_eq!(caps.capture_permission, "unavailable"); + let err = backend + .capture(&Target::Desktop, &CaptureCaps::default()) + .expect_err("capture must fail without the pipewire feature"); + assert_eq!(err.code.as_str(), "CaptureFailed"); + } } diff --git a/docs/computer-use.md b/docs/computer-use.md index e5c095091..86b5e4772 100644 --- a/docs/computer-use.md +++ b/docs/computer-use.md @@ -131,11 +131,11 @@ await wait( | ----------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | macOS x64/arm64 | ScreenCapture/Quartz plus native AX and input. Grant Screen Recording for capture and Accessibility for input/AX, then restart the launching host. | | Linux X11 x64/arm64 | X11 capture/input and AT-SPI accessibility. Requires a readable display plus RandR/XTEST. | -| Linux Wayland x64/arm64 | ScreenCast portal/PipeWire capture, RemoteDesktop portal or `LIBEI_SOCKET` input, and AT-SPI accessibility. Portal permission prompts and compositor restrictions apply; background per-window native input is unavailable. | +| Linux Wayland x64/arm64 | RemoteDesktop portal or `LIBEI_SOCKET` input and AT-SPI accessibility. ScreenCast portal/PipeWire capture ships only in builds compiled with the `wayland-pipewire` Cargo feature; released binaries omit it, so `capabilities()` reports `capture: false` there. Portal permission prompts and compositor restrictions apply; background per-window native input is unavailable. | | Windows x64 | Native display/window capture, Win32 input, and UI Automation accessibility. | | Other published targets | Unsupported unless the native addon reports capabilities. | -Inspect `desktop.capabilities()` rather than assuming capture, input, AX, or permission state. On Wayland, a missing portal/PipeWire feature or denied RemoteDesktop portal is reported as a capture/input/permission failure rather than falling back to X11. +Inspect `desktop.capabilities()` rather than assuming capture, input, AX, or permission state. On Wayland, released builds are compiled without the `wayland-pipewire` feature, so `capabilities()` reports `capture: false`; where the feature is present, a missing portal/PipeWire feature or denied RemoteDesktop portal is reported as a capture/input/permission failure rather than falling back to X11. ## Safety and troubleshooting diff --git a/packages/natives/CHANGELOG.md b/packages/natives/CHANGELOG.md index eef2c690a..890097735 100644 --- a/packages/natives/CHANGELOG.md +++ b/packages/natives/CHANGELOG.md @@ -2,6 +2,10 @@ ## [Unreleased] +### Fixed + +- Fixed Wayland `desktop.capabilities()` reporting `capture: true` on builds compiled without the `wayland-pipewire` feature (all released binaries), where every capture call hard-fails with `CaptureFailed`. The `capture` flag and `capture_permission` now reflect the compiled-in feature ([#7700](https://github.com/can1357/oh-my-pi/issues/7700)). + ## [17.2.9] - 2026-08-05 ### Changed