fix(dbus): stop forcing the zbus 5 executor onto tokio

zbus features unify across the dependency graph and "tokio" wins over
"async-io": enabling it via rfd, ashpd (secret portal) and ksni's
default features flipped the shared zbus 5 executor to tokio for
accesskit_unix/atspi too — Slint's accessibility backend opens its
D-Bus connection on a plain thread and panicked at startup ("there is
no reactor running, must be called from the context of a Tokio 1.x
runtime"), killing AT-SPI support. Switch every zbus-5 consumer we
control to the async-io backend (rfd "async-std", ashpd "async-io",
ksni default-features off + "async-io"): async-io connections
self-drive from any thread and the futures still await fine inside our
tokio runtime. The zbus 4 group (keyring/secret-service/mpris-server/
qbz-audio) is a separate feature graph and stays on tokio by design.

cc
This commit is contained in:
vicrodh committed 2026-07-05 10:26:18 -06:00
1 parent c482ca0c0b
commit 6c9964481a
4 files changed
+41 -10

No files matched your search

+18 -4
View File
@@ -411,6 +411,8 @@ version = "0.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d2f3f79755c74fd155000314eb349864caa787c6592eace6c6882dad873d9c39"
dependencies = [
"async-fs",
"async-net",
"enumflags2",
"futures-channel",
"futures-util",
@@ -418,7 +420,6 @@ dependencies = [
"raw-window-handle",
"serde",
"serde_repr",
"tokio",
"url",
"wayland-backend",
"wayland-client",
@@ -436,7 +437,6 @@ dependencies = [
"futures-util",
"getrandom 0.4.2",
"serde",
"tokio",
"zbus 5.15.0",
]
@@ -518,6 +518,17 @@ dependencies = [
"pin-project-lite",
]
[[package]]
name = "async-net"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b948000fad4873c1c9339d60f2623323a0cfd3816e5181033c6a5cb68b2accf7"
dependencies = [
"async-io",
"blocking",
"futures-lite",
]
[[package]]
name = "async-process"
version = "2.5.0"
@@ -3683,11 +3694,15 @@ version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a7ca513d0be42df5edb485af9f44a12b2cb85af773d91c27dc796d1c58b78edc"
dependencies = [
"async-executor",
"async-io",
"async-lock",
"futures-channel",
"futures-lite",
"futures-util",
"once_cell",
"pastey 0.2.3",
"serde",
"tokio",
"zbus 5.15.0",
]
@@ -9898,7 +9913,6 @@ dependencies = [
"rustix 1.1.4",
"serde",
"serde_repr",
"tokio",
"tracing",
"uds_windows",
"uuid 1.22.0",
+7 -1
View File
@@ -19,4 +19,10 @@ dirs = "6"
keyring = { version = "3", features = ["async-secret-service", "crypto-rust"] }
[target.'cfg(target_os = "linux")'.dependencies]
ashpd = { version = "0.13", default-features = false, features = ["tokio", "secret"] }
# async-io, NOT tokio: zbus features unify across the whole dependency graph
# and its "tokio" feature wins over "async-io" — enabling it here flipped the
# zbus 5 executor to tokio for accesskit_unix/atspi/ksni too, whose D-Bus
# connections live on plain threads => startup panic "there is no reactor
# running" and dead AT-SPI accessibility. async-io futures are runtime-
# agnostic, so our portal call still awaits fine inside tokio.
ashpd = { version = "0.13", default-features = false, features = ["async-io", "secret"] }
+7 -3
View File
@@ -30,10 +30,14 @@ urlencoding = "2"
[target.'cfg(target_os = "linux")'.dependencies]
mpris-server = "0.8"
async-channel = "2"
# XDG notification portal (goes over D-Bus). Same crate + tokio runtime the
# secret portal already pulls in (qbz-credentials uses ashpd 0.13/tokio).
# XDG notification portal (goes over D-Bus). async-io, NOT tokio: zbus 5's
# "tokio" feature unifies graph-wide and its executor then requires a tokio
# reactor on EVERY thread that opens a zbus connection — accesskit_unix/atspi
# (Slint accessibility) and ksni run theirs on plain threads and panicked at
# startup ("there is no reactor running"). async-io futures still await fine
# inside our tokio runtime. Keep in sync with qbz-credentials/Cargo.toml.
ashpd = { version = "0.13", default-features = false, features = [
"tokio",
"async-io",
"notification",
] }
+9 -2
View File
@@ -59,7 +59,10 @@ image = { version = "0.25", default-features = false, features = ["jpeg", "png"]
urlencoding = "2"
open = "5"
arboard = "3"
rfd = { version = "0.15", default-features = false, features = ["xdg-portal", "tokio"] }
# async-std (rfd's name for the async-io/ashpd backend), NOT tokio: zbus 5's
# "tokio" feature unifies graph-wide and breaks accesskit/atspi/ksni threads
# (see the ashpd comment in qbz-credentials/Cargo.toml).
rfd = { version = "0.15", default-features = false, features = ["xdg-portal", "async-std"] }
serde = { workspace = true }
serde_json = { workspace = true }
dirs = "5"
@@ -126,7 +129,11 @@ i-slint-renderer-femtovg = { version = "=1.16.1", features = ["wgpu"] }
# Activate / SecondaryActivate / Scroll. macOS/Windows use `tray-icon` instead
# (added with the CustomApplicationHandler slice).
[target.'cfg(target_os = "linux")'.dependencies]
ksni = { version = "0.3", features = ["blocking"] }
# default-features = false: ksni's default is "tokio", which flips the shared
# zbus 5 executor to tokio graph-wide (breaks accesskit/atspi plain-thread
# connections — see the rfd comment above). We only use the blocking API on
# worker threads; async-io self-drives the connection from any context.
ksni = { version = "0.3", default-features = false, features = ["blocking", "async-io"] }
# macOS menu-bar tray (NSStatusItem), hand-rolled directly on objc2 0.5 /
# app-kit 0.2 — the SAME objc2 generation winit 0.30 uses — so the NSMenu lives