fix(backend_detect): use raw zbus for portal check to avoid OnceLock connection poisoning
ashpd caches zbus::Connection in a global OnceLock. When check_portal_available() created a Screencast proxy, the connection was cached there. When the function returned and its tokio Runtime dropped, the cached connection became dead. Subsequent setup_portal() calls reused this dead connection and hung forever. Fix: replace ashpd Screencast proxy with direct zbus D-Bus interface check, which does not touch the ashpd global connection cache. Add examples/test_portal.rs for minimal Portal ScreenCast testing.
This commit is contained in:
1
Cargo.lock
generated
1
Cargo.lock
generated
@@ -1788,6 +1788,7 @@ dependencies = [
|
|||||||
"wayland-client",
|
"wayland-client",
|
||||||
"wayland-protocols",
|
"wayland-protocols",
|
||||||
"wayland-protocols-wlr",
|
"wayland-protocols-wlr",
|
||||||
|
"zbus",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ drm = "0.12"
|
|||||||
drm-fourcc = "2"
|
drm-fourcc = "2"
|
||||||
libc = "0.2"
|
libc = "0.2"
|
||||||
ashpd = { version = "0.13", features = ["tokio", "screencast"] }
|
ashpd = { version = "0.13", features = ["tokio", "screencast"] }
|
||||||
|
zbus = { version = "5", default-features = false, features = ["tokio"] }
|
||||||
tokio = { version = "1", features = ["rt"] }
|
tokio = { version = "1", features = ["rt"] }
|
||||||
pipewire = "0.9"
|
pipewire = "0.9"
|
||||||
libspa = "0.9"
|
libspa = "0.9"
|
||||||
|
|||||||
68
examples/test_portal.rs
Normal file
68
examples/test_portal.rs
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
use ashpd::desktop::screencast::{CursorMode, Screencast, SelectSourcesOptions, SourceType};
|
||||||
|
use ashpd::desktop::PersistMode;
|
||||||
|
use ashpd::enumflags2::BitFlags;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let rt = tokio::runtime::Runtime::new().unwrap();
|
||||||
|
rt.block_on(async {
|
||||||
|
eprintln!("1. Creating Screencast proxy...");
|
||||||
|
let proxy = match Screencast::new().await {
|
||||||
|
Ok(p) => {
|
||||||
|
eprintln!(" OK");
|
||||||
|
p
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
eprintln!(" FAIL: {e}");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
eprintln!("2. Creating session...");
|
||||||
|
let session = match proxy.create_session(Default::default()).await {
|
||||||
|
Ok(s) => {
|
||||||
|
eprintln!(" OK");
|
||||||
|
s
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
eprintln!(" FAIL: {e}");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
eprintln!("3. Selecting sources...");
|
||||||
|
let sources: BitFlags<SourceType> = SourceType::Monitor.into();
|
||||||
|
let result = proxy
|
||||||
|
.select_sources(
|
||||||
|
&session,
|
||||||
|
SelectSourcesOptions::default()
|
||||||
|
.set_cursor_mode(CursorMode::Embedded)
|
||||||
|
.set_sources(sources)
|
||||||
|
.set_multiple(false)
|
||||||
|
.set_persist_mode(PersistMode::DoNot),
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
match result {
|
||||||
|
Ok(_) => eprintln!(" OK"),
|
||||||
|
Err(e) => {
|
||||||
|
eprintln!(" FAIL: {e}");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
eprintln!("4. Starting (should show dialog)...");
|
||||||
|
let response = match proxy.start(&session, None, Default::default()).await {
|
||||||
|
Ok(r) => {
|
||||||
|
eprintln!(" OK");
|
||||||
|
r
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
eprintln!(" FAIL: {e}");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
match response.response() {
|
||||||
|
Ok(r) => eprintln!(" Got {} stream(s)", r.streams().len()),
|
||||||
|
Err(e) => eprintln!(" Response error: {e}"),
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -37,11 +37,10 @@ impl Dispatch<WlRegistry, GlobalListContents> for RegistryLs {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 通过 D-Bus 检测 XDG Desktop Portal 的 ScreenCast 接口是否可用
|
// CAUTION: must NOT use ashpd here — ashpd caches zbus::Connection in a global
|
||||||
// 尝试创建 Screencast proxy,如果 Portal 服务未运行则返回 false
|
// OnceLock; if the tokio runtime owning that connection is dropped before
|
||||||
|
// setup_portal() runs, the cached connection becomes dead and hangs forever.
|
||||||
fn check_portal_available() -> bool {
|
fn check_portal_available() -> bool {
|
||||||
use ashpd::desktop::screencast::Screencast;
|
|
||||||
|
|
||||||
let rt = match tokio::runtime::Runtime::new() {
|
let rt = match tokio::runtime::Runtime::new() {
|
||||||
Ok(rt) => rt,
|
Ok(rt) => rt,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
@@ -51,30 +50,43 @@ fn check_portal_available() -> bool {
|
|||||||
};
|
};
|
||||||
|
|
||||||
rt.block_on(async {
|
rt.block_on(async {
|
||||||
let proxy = match Screencast::new().await {
|
let conn = match zbus::Connection::session().await {
|
||||||
Ok(p) => p,
|
Ok(c) => c,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
tracing::info!("Portal not available: {e}");
|
tracing::info!("D-Bus session bus unavailable: {e}");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Verify the portal actually exposes ScreenCast capabilities,
|
let inner: zbus::Proxy = match zbus::proxy::Builder::new(&conn)
|
||||||
// not just that the D-Bus service is running.
|
.destination("org.freedesktop.portal.Desktop")
|
||||||
match proxy.available_source_types().await {
|
.and_then(|b| b.path("/org/freedesktop/portal/desktop"))
|
||||||
Ok(types) if !types.is_empty() => {
|
.and_then(|b| b.interface("org.freedesktop.portal.ScreenCast"))
|
||||||
tracing::info!("Portal ScreenCast available (source types: {types:?})");
|
{
|
||||||
|
Ok(b) => match b.build().await {
|
||||||
|
Ok(p) => p,
|
||||||
|
Err(e) => {
|
||||||
|
tracing::info!("Portal ScreenCast interface not available: {e}");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Err(e) => {
|
||||||
|
tracing::info!("Portal ScreenCast proxy build failed: {e}");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let version = match inner.get_property::<u32>("version").await {
|
||||||
|
Ok(version) => {
|
||||||
|
tracing::info!("Portal ScreenCast available (version: {version})");
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
Ok(types) => {
|
|
||||||
tracing::info!("Portal ScreenCast proxy exists but no source types available ({types:?})");
|
|
||||||
false
|
|
||||||
}
|
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
tracing::info!("Portal ScreenCast available_source_types query failed: {e}");
|
tracing::info!("Portal ScreenCast version query failed: {e}");
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
version
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user