diff --git a/Cargo.lock b/Cargo.lock index 489ca9b6f..9c224bfa1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -64,7 +64,9 @@ dependencies = [ "async-channel", "atspi", "futures-lite", + "once_cell", "serde", + "tokio", "zbus", ] @@ -353,6 +355,12 @@ version = "1.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" +[[package]] +name = "bytes" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" + [[package]] name = "calloop" version = "0.10.5" @@ -717,6 +725,15 @@ version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" +[[package]] +name = "hermit-abi" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7" +dependencies = [ + "libc", +] + [[package]] name = "hermit-abi" version = "0.3.1" @@ -757,7 +774,7 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "76e86b86ae312accbf05ade23ce76b625e0e47a255712b7414037385a1c05380" dependencies = [ - "hermit-abi", + "hermit-abi 0.3.1", "libc", "windows-sys 0.45.0", ] @@ -967,6 +984,16 @@ dependencies = [ "minimal-lexical", ] +[[package]] +name = "num_cpus" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b" +dependencies = [ + "hermit-abi 0.2.6", + "libc", +] + [[package]] name = "num_enum" version = "0.5.11" @@ -1498,6 +1525,23 @@ dependencies = [ "strict-num", ] +[[package]] +name = "tokio" +version = "1.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0de47a4eecbe11f498978a9b29d792f0d2692d1dd003650c24c76510e3bc001" +dependencies = [ + "autocfg", + "bytes", + "libc", + "mio", + "num_cpus", + "pin-project-lite", + "socket2", + "tracing", + "windows-sys 0.45.0", +] + [[package]] name = "toml_datetime" version = "0.6.1" @@ -1965,6 +2009,7 @@ dependencies = [ "futures-sink", "futures-util", "hex", + "lazy_static", "nix 0.26.2", "once_cell", "ordered-stream", @@ -1973,6 +2018,7 @@ dependencies = [ "serde_repr", "sha1", "static_assertions", + "tokio", "tracing", "uds_windows", "winapi", diff --git a/platforms/unix/Cargo.toml b/platforms/unix/Cargo.toml index c17c888e7..befd92721 100644 --- a/platforms/unix/Cargo.toml +++ b/platforms/unix/Cargo.toml @@ -10,12 +10,18 @@ repository = "https://github.com/AccessKit/accesskit" readme = "README.md" edition = "2021" +[features] +default = ["async-io"] +async-io = ["zbus/async-io"] +tokio = ["dep:once_cell", "dep:tokio", "zbus/tokio"] + [dependencies] accesskit = { version = "0.11.0", path = "../../common" } accesskit_consumer = { version = "0.15.0", path = "../../consumer" } async-channel = "1.8.0" -atspi = "0.10.1" +atspi = { version = "0.10.1", default-features = false } futures-lite = "1.12.0" +once_cell = { version = "1.17.1", optional = true } serde = "1.0" -zbus = "3.6" - +tokio = { version = "1.10.0", optional = true, features = ["rt-multi-thread", "net", "time"] } +zbus = { version = "3.6", default-features = false } diff --git a/platforms/unix/src/adapter.rs b/platforms/unix/src/adapter.rs index 53e901266..a549d7fe3 100644 --- a/platforms/unix/src/adapter.rs +++ b/platforms/unix/src/adapter.rs @@ -13,7 +13,7 @@ use crate::{ }, context::Context, node::{filter, filter_detached, NodeWrapper, PlatformNode}, - util::AppContext, + util::{block_on, AppContext}, }; use accesskit::{ActionHandler, NodeId, Rect, Role, TreeUpdate}; use accesskit_consumer::{DetachedNode, FilterResult, Node, Tree, TreeChangeHandler, TreeState}; @@ -39,10 +39,12 @@ impl Adapter { initial_state: impl 'static + FnOnce() -> TreeUpdate, action_handler: Box, ) -> Option { - let mut atspi_bus = Bus::a11y_bus()?; + let mut atspi_bus = block_on(async { Bus::a11y_bus().await })?; let (event_sender, event_receiver) = async_channel::unbounded(); let atspi_bus_copy = atspi_bus.clone(); - let event_task = atspi_bus.connection().inner().executor().spawn( + #[cfg(feature = "tokio")] + let _guard = crate::util::TOKIO_RT.enter(); + let event_task = atspi_bus.connection().executor().spawn( async move { handle_events(atspi_bus_copy, event_receiver).await; }, @@ -51,7 +53,7 @@ impl Adapter { let tree = Tree::new(initial_state()); let app_context = AppContext::new(app_name, toolkit_name, toolkit_version); let context = Context::new(tree, action_handler, app_context); - atspi_bus.register_root_node(&context).ok()?; + block_on(async { atspi_bus.register_root_node(&context).await.ok() })?; let adapter = Adapter { atspi_bus, _event_task: event_task, @@ -85,31 +87,47 @@ impl Adapter { fn register_interfaces(&self, id: NodeId, new_interfaces: InterfaceSet) -> zbus::Result { let path = format!("{}{}", ACCESSIBLE_PATH_PREFIX, ObjectId::from(id).as_str()); if new_interfaces.contains(Interface::Accessible) { - self.atspi_bus.register_interface( - &path, - AccessibleInterface::new( - self.atspi_bus.unique_name().to_owned(), - PlatformNode::new(&self.context, id), - ), - )?; + block_on(async { + self.atspi_bus + .register_interface( + &path, + AccessibleInterface::new( + self.atspi_bus.unique_name().to_owned(), + PlatformNode::new(&self.context, id), + ), + ) + .await + })?; } if new_interfaces.contains(Interface::Action) { - self.atspi_bus.register_interface( - &path, - ActionInterface::new(PlatformNode::new(&self.context, id)), - )?; + block_on(async { + self.atspi_bus + .register_interface( + &path, + ActionInterface::new(PlatformNode::new(&self.context, id)), + ) + .await + })?; } if new_interfaces.contains(Interface::Component) { - self.atspi_bus.register_interface( - &path, - ComponentInterface::new(PlatformNode::new(&self.context, id)), - )?; + block_on(async { + self.atspi_bus + .register_interface( + &path, + ComponentInterface::new(PlatformNode::new(&self.context, id)), + ) + .await + })?; } if new_interfaces.contains(Interface::Value) { - self.atspi_bus.register_interface( - &path, - ValueInterface::new(PlatformNode::new(&self.context, id)), - )?; + block_on(async { + self.atspi_bus + .register_interface( + &path, + ValueInterface::new(PlatformNode::new(&self.context, id)), + ) + .await + })?; } Ok(true) } @@ -119,24 +137,30 @@ impl Adapter { id: &ObjectId, old_interfaces: InterfaceSet, ) -> zbus::Result { - let path = format!("{}{}", ACCESSIBLE_PATH_PREFIX, id.as_str()); - if old_interfaces.contains(Interface::Accessible) { - self.atspi_bus - .unregister_interface::>(&path)?; - } - if old_interfaces.contains(Interface::Action) { - self.atspi_bus - .unregister_interface::(&path)?; - } - if old_interfaces.contains(Interface::Component) { - self.atspi_bus - .unregister_interface::(&path)?; - } - if old_interfaces.contains(Interface::Value) { - self.atspi_bus - .unregister_interface::(&path)?; - } - Ok(true) + block_on(async { + let path = format!("{}{}", ACCESSIBLE_PATH_PREFIX, id.as_str()); + if old_interfaces.contains(Interface::Accessible) { + self.atspi_bus + .unregister_interface::>(&path) + .await?; + } + if old_interfaces.contains(Interface::Action) { + self.atspi_bus + .unregister_interface::(&path) + .await?; + } + if old_interfaces.contains(Interface::Component) { + self.atspi_bus + .unregister_interface::(&path) + .await?; + } + if old_interfaces.contains(Interface::Value) { + self.atspi_bus + .unregister_interface::(&path) + .await?; + } + Ok(true) + }) } pub fn set_root_window_bounds(&self, outer: Rect, inner: Rect) { diff --git a/platforms/unix/src/atspi/bus.rs b/platforms/unix/src/atspi/bus.rs index cfe08f3c8..1e5d55d76 100644 --- a/platforms/unix/src/atspi/bus.rs +++ b/platforms/unix/src/atspi/bus.rs @@ -8,26 +8,25 @@ use crate::{ context::Context, PlatformRootNode, }; -use atspi::{bus::BusProxyBlocking, socket::SocketProxyBlocking, EventBody}; +use atspi::{bus::BusProxy, socket::SocketProxy, EventBody}; use serde::Serialize; use std::{collections::HashMap, env::var, sync::Arc}; use zbus::{ - blocking::{Connection, ConnectionBuilder}, names::{BusName, InterfaceName, MemberName, OwnedUniqueName}, zvariant::{ObjectPath, Str, Value}, - Address, Result, + Address, Connection, ConnectionBuilder, Result, }; #[derive(Clone)] pub(crate) struct Bus { conn: Connection, - socket_proxy: SocketProxyBlocking<'static>, + socket_proxy: SocketProxy<'static>, } impl Bus { - pub fn a11y_bus() -> Option { - let conn = a11y_bus()?; - let socket_proxy = SocketProxyBlocking::new(&conn).ok()?; + pub async fn a11y_bus() -> Option { + let conn = a11y_bus().await?; + let socket_proxy = SocketProxy::new(&conn).await.ok()?; Some(Bus { conn, socket_proxy }) } @@ -39,36 +38,44 @@ impl Bus { self.conn.unique_name().unwrap() } - pub fn register_interface(&self, path: &str, interface: T) -> Result + pub async fn register_interface(&self, path: &str, interface: T) -> Result where T: zbus::Interface, { - self.conn.object_server().at(path, interface) + self.conn.object_server().at(path, interface).await } - pub fn unregister_interface(&self, path: &str) -> Result + pub async fn unregister_interface(&self, path: &str) -> Result where T: zbus::Interface, { - self.conn.object_server().remove::(path) + self.conn.object_server().remove::(path).await } - pub fn register_root_node(&mut self, context: &Arc) -> Result { + pub async fn register_root_node(&mut self, context: &Arc) -> Result { let node = PlatformRootNode::new(context); let path = format!("{}{}", ACCESSIBLE_PATH_PREFIX, ObjectId::root().as_str()); let registered = self .conn .object_server() - .at(path.clone(), ApplicationInterface(node.clone()))? - && self.conn.object_server().at( - path, - AccessibleInterface::new(self.unique_name().to_owned(), node), - )?; + .at(path.clone(), ApplicationInterface(node.clone())) + .await? + && self + .conn + .object_server() + .at( + path, + AccessibleInterface::new(self.unique_name().to_owned(), node), + ) + .await?; if registered { - let desktop = self.socket_proxy.embed(&( - self.unique_name().as_str(), - ObjectPath::from_str_unchecked(ROOT_PATH), - ))?; + let desktop = self + .socket_proxy + .embed(&( + self.unique_name().as_str(), + ObjectPath::from_str_unchecked(ROOT_PATH), + )) + .await?; context.app_context.write().unwrap().desktop_address = Some(desktop.into()); Ok(true) } else { @@ -219,7 +226,6 @@ impl Bus { ) -> Result<()> { let path = format!("{}{}", ACCESSIBLE_PATH_PREFIX, id.as_str()); self.conn - .inner() .emit_signal( Option::::None, path, @@ -231,17 +237,19 @@ impl Bus { } } -fn a11y_bus() -> Option { +async fn a11y_bus() -> Option { let address = match var("AT_SPI_BUS_ADDRESS") { Ok(address) if !address.is_empty() => address, _ => { - let session_bus = Connection::session().ok()?; - BusProxyBlocking::new(&session_bus) + let session_bus = Connection::session().await.ok()?; + BusProxy::new(&session_bus) + .await .ok()? .get_address() + .await .ok()? } }; let address: Address = address.as_str().try_into().ok()?; - ConnectionBuilder::address(address).ok()?.build().ok() + ConnectionBuilder::address(address).ok()?.build().await.ok() } diff --git a/platforms/unix/src/util.rs b/platforms/unix/src/util.rs index 995b57a9b..a8a7fc16e 100644 --- a/platforms/unix/src/util.rs +++ b/platforms/unix/src/util.rs @@ -6,6 +6,28 @@ use crate::atspi::OwnedObjectAddress; use accesskit::{Point, Rect}; use atspi::CoordType; +#[cfg(feature = "tokio")] +use once_cell::sync::Lazy; + +#[cfg(not(feature = "tokio"))] +pub(crate) fn block_on(future: F) -> F::Output { + zbus::block_on(future) +} + +#[cfg(feature = "tokio")] +pub(crate) static TOKIO_RT: Lazy = Lazy::new(|| { + tokio::runtime::Builder::new_multi_thread() + .worker_threads(1) + .enable_io() + .enable_time() + .build() + .expect("launch of single-threaded tokio runtime") +}); + +#[cfg(feature = "tokio")] +pub(crate) fn block_on(future: F) -> F::Output { + TOKIO_RT.block_on(future) +} pub(crate) struct AppContext { pub(crate) name: String, diff --git a/platforms/winit/Cargo.toml b/platforms/winit/Cargo.toml index 0f9a98cf0..2bea8226a 100644 --- a/platforms/winit/Cargo.toml +++ b/platforms/winit/Cargo.toml @@ -11,7 +11,9 @@ readme = "README.md" edition = "2021" [features] -default = ["accesskit_unix"] +default = ["accesskit_unix", "async-io"] +async-io = ["accesskit_unix/async-io"] +tokio = ["accesskit_unix/tokio"] [dependencies] accesskit = { version = "0.11.0", path = "../../common" } @@ -24,8 +26,7 @@ accesskit_windows = { version = "0.14.0", path = "../windows" } accesskit_macos = { version = "0.7.0", path = "../macos" } [target.'cfg(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd", target_os = "openbsd", target_os = "netbsd"))'.dependencies] -accesskit_unix = { version = "0.4.0", path = "../unix", optional = true } +accesskit_unix = { version = "0.4.0", path = "../unix", optional = true, default-features = false } [dev-dependencies] winit = "0.28" -