Skip to content

Commit

Permalink
feat: Add features for async runtimes on Unix (#248)
Browse files Browse the repository at this point in the history
  • Loading branch information
DataTriny authored May 21, 2023
1 parent 9829d0d commit b56b4ea
Show file tree
Hide file tree
Showing 6 changed files with 181 additions and 74 deletions.
48 changes: 47 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 9 additions & 3 deletions platforms/unix/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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 }
106 changes: 65 additions & 41 deletions platforms/unix/src/adapter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand All @@ -39,10 +39,12 @@ impl Adapter {
initial_state: impl 'static + FnOnce() -> TreeUpdate,
action_handler: Box<dyn ActionHandler + Send + Sync>,
) -> Option<Self> {
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;
},
Expand All @@ -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,
Expand Down Expand Up @@ -85,31 +87,47 @@ impl Adapter {
fn register_interfaces(&self, id: NodeId, new_interfaces: InterfaceSet) -> zbus::Result<bool> {
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)
}
Expand All @@ -119,24 +137,30 @@ impl Adapter {
id: &ObjectId,
old_interfaces: InterfaceSet,
) -> zbus::Result<bool> {
let path = format!("{}{}", ACCESSIBLE_PATH_PREFIX, id.as_str());
if old_interfaces.contains(Interface::Accessible) {
self.atspi_bus
.unregister_interface::<AccessibleInterface<PlatformNode>>(&path)?;
}
if old_interfaces.contains(Interface::Action) {
self.atspi_bus
.unregister_interface::<ActionInterface>(&path)?;
}
if old_interfaces.contains(Interface::Component) {
self.atspi_bus
.unregister_interface::<ComponentInterface>(&path)?;
}
if old_interfaces.contains(Interface::Value) {
self.atspi_bus
.unregister_interface::<ValueInterface>(&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::<AccessibleInterface<PlatformNode>>(&path)
.await?;
}
if old_interfaces.contains(Interface::Action) {
self.atspi_bus
.unregister_interface::<ActionInterface>(&path)
.await?;
}
if old_interfaces.contains(Interface::Component) {
self.atspi_bus
.unregister_interface::<ComponentInterface>(&path)
.await?;
}
if old_interfaces.contains(Interface::Value) {
self.atspi_bus
.unregister_interface::<ValueInterface>(&path)
.await?;
}
Ok(true)
})
}

pub fn set_root_window_bounds(&self, outer: Rect, inner: Rect) {
Expand Down
60 changes: 34 additions & 26 deletions platforms/unix/src/atspi/bus.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<Self> {
let conn = a11y_bus()?;
let socket_proxy = SocketProxyBlocking::new(&conn).ok()?;
pub async fn a11y_bus() -> Option<Self> {
let conn = a11y_bus().await?;
let socket_proxy = SocketProxy::new(&conn).await.ok()?;
Some(Bus { conn, socket_proxy })
}

Expand All @@ -39,36 +38,44 @@ impl Bus {
self.conn.unique_name().unwrap()
}

pub fn register_interface<T>(&self, path: &str, interface: T) -> Result<bool>
pub async fn register_interface<T>(&self, path: &str, interface: T) -> Result<bool>
where
T: zbus::Interface,
{
self.conn.object_server().at(path, interface)
self.conn.object_server().at(path, interface).await
}

pub fn unregister_interface<T>(&self, path: &str) -> Result<bool>
pub async fn unregister_interface<T>(&self, path: &str) -> Result<bool>
where
T: zbus::Interface,
{
self.conn.object_server().remove::<T, _>(path)
self.conn.object_server().remove::<T, _>(path).await
}

pub fn register_root_node(&mut self, context: &Arc<Context>) -> Result<bool> {
pub async fn register_root_node(&mut self, context: &Arc<Context>) -> Result<bool> {
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 {
Expand Down Expand Up @@ -219,7 +226,6 @@ impl Bus {
) -> Result<()> {
let path = format!("{}{}", ACCESSIBLE_PATH_PREFIX, id.as_str());
self.conn
.inner()
.emit_signal(
Option::<BusName>::None,
path,
Expand All @@ -231,17 +237,19 @@ impl Bus {
}
}

fn a11y_bus() -> Option<Connection> {
async fn a11y_bus() -> Option<Connection> {
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()
}
Loading

0 comments on commit b56b4ea

Please sign in to comment.