#[cfg(all(not(feature = "async-std"), not(feature = "tokio")))]
compile_error!("You must specify at least one of the `async-std` or `tokio` features.");
pub use atspi_common as common;
use atspi_proxies::{
bus::{BusProxy, StatusProxy},
registry::RegistryProxy,
};
use common::error::AtspiError;
use common::events::{Event, GenericEvent, HasMatchRule, HasRegistryEventString};
use futures_lite::stream::{Stream, StreamExt};
use std::ops::Deref;
use zbus::{fdo::DBusProxy, Address, MatchRule, MessageStream, MessageType};
pub type AtspiResult<T> = std::result::Result<T, AtspiError>;
pub struct AccessibilityConnection {
registry: RegistryProxy<'static>,
dbus_proxy: DBusProxy<'static>,
}
impl AccessibilityConnection {
#[cfg_attr(feature = "tracing", tracing::instrument)]
pub async fn open() -> zbus::Result<Self> {
let a11y_bus_addr = {
#[cfg(feature = "tracing")]
tracing::debug!("Connecting to session bus");
let session_bus = Box::pin(zbus::Connection::session()).await?;
#[cfg(feature = "tracing")]
tracing::debug!(
name = session_bus.unique_name().map(|n| n.as_str()),
"Connected to session bus"
);
let proxy = BusProxy::new(&session_bus).await?;
#[cfg(feature = "tracing")]
tracing::debug!("Getting a11y bus address from session bus");
proxy.get_address().await?
};
#[cfg(feature = "tracing")]
tracing::debug!(address = %a11y_bus_addr, "Got a11y bus address");
let addr: Address = a11y_bus_addr.parse()?;
Self::connect(addr).await
}
pub async fn connect(bus_addr: Address) -> zbus::Result<Self> {
#[cfg(feature = "tracing")]
tracing::debug!("Connecting to a11y bus");
let bus = Box::pin(zbus::ConnectionBuilder::address(bus_addr)?.build()).await?;
#[cfg(feature = "tracing")]
tracing::debug!(name = bus.unique_name().map(|n| n.as_str()), "Connected to a11y bus");
let registry = RegistryProxy::new(&bus).await?;
let dbus_proxy = DBusProxy::new(registry.connection()).await?;
Ok(Self { registry, dbus_proxy })
}
pub fn event_stream(&self) -> impl Stream<Item = Result<Event, AtspiError>> {
MessageStream::from(self.registry.connection()).filter_map(|res| {
let msg = match res {
Ok(m) => m,
Err(e) => return Some(Err(e.into())),
};
match msg.message_type() {
MessageType::Signal => Some(Event::try_from(&*msg)),
_ => None,
}
})
}
pub async fn add_match_rule<T: HasMatchRule>(&self) -> Result<(), AtspiError> {
let match_rule = MatchRule::try_from(<T as HasMatchRule>::MATCH_RULE_STRING)?;
self.dbus_proxy.add_match_rule(match_rule).await?;
Ok(())
}
pub async fn remove_match_rule<T: HasMatchRule>(&self) -> Result<(), AtspiError> {
let match_rule = MatchRule::try_from(<T as HasMatchRule>::MATCH_RULE_STRING)?;
self.dbus_proxy.add_match_rule(match_rule).await?;
Ok(())
}
pub async fn add_registry_event<T: HasRegistryEventString>(&self) -> Result<(), AtspiError> {
self.registry
.register_event(<T as HasRegistryEventString>::REGISTRY_EVENT_STRING)
.await?;
Ok(())
}
pub async fn remove_registry_event<T: HasRegistryEventString>(&self) -> Result<(), AtspiError> {
self.registry
.deregister_event(<T as HasRegistryEventString>::REGISTRY_EVENT_STRING)
.await?;
Ok(())
}
pub async fn register_event<T: HasRegistryEventString + HasMatchRule>(
&self,
) -> Result<(), AtspiError> {
self.add_registry_event::<T>().await?;
self.add_match_rule::<T>().await?;
Ok(())
}
pub async fn deregister_event<T: HasRegistryEventString + HasMatchRule>(
&self,
) -> Result<(), AtspiError> {
self.remove_registry_event::<T>().await?;
self.remove_match_rule::<T>().await?;
Ok(())
}
#[must_use = "The reference to the underlying zbus::Connection must be used"]
pub fn connection(&self) -> &zbus::Connection {
self.registry.connection()
}
pub async fn send_event<T>(&self, event: T) -> Result<u32, AtspiError>
where
T: for<'a> GenericEvent<'a>,
{
let conn = self.connection();
let new_message = zbus::MessageBuilder::signal(
event.path(),
<T as GenericEvent>::DBUS_INTERFACE,
<T as GenericEvent>::DBUS_MEMBER,
)?
.sender(conn.unique_name().ok_or(AtspiError::MissingName)?)?
.build(&event.body())?;
Ok(conn.send_message(new_message).await?)
}
}
impl Deref for AccessibilityConnection {
type Target = RegistryProxy<'static>;
fn deref(&self) -> &Self::Target {
&self.registry
}
}
pub async fn set_session_accessibility(status: bool) -> std::result::Result<(), AtspiError> {
let session = Box::pin(zbus::Connection::session()).await?;
let status_proxy = StatusProxy::new(&session).await?;
if status_proxy.is_enabled().await? != status {
status_proxy.set_is_enabled(status).await?;
}
Ok(())
}
pub async fn read_session_accessibility() -> AtspiResult<bool> {
let session = Box::pin(zbus::Connection::session()).await?;
let status_proxy = StatusProxy::new(&session).await?;
status_proxy.is_enabled().await.map_err(Into::into)
}