pub mod document;
pub mod focus;
pub mod keyboard;
pub mod mouse;
pub mod object;
pub mod terminal;
pub mod window;
use std::{collections::HashMap, sync::Arc};
use serde::{Deserialize, Serialize};
use zbus::{
names::{InterfaceName, MemberName, OwnedUniqueName, UniqueName},
zvariant::{self, OwnedObjectPath, OwnedValue, Signature, Type, Value},
Message,
};
use crate::{
cache::CacheItem,
connection,
identify::{
document::DocumentEvents, focus::FocusEvents, keyboard::KeyboardEvents, mouse::MouseEvents,
object::ObjectEvents, terminal::TerminalEvents, window::WindowEvents,
},
AtspiError,
};
use atspi_macros::{try_from_zbus_message, GenericEvent};
#[derive(Debug, Serialize, Deserialize)]
pub struct EventBody<'a, T> {
#[serde(rename = "type")]
pub kind: T,
pub detail1: i32,
pub detail2: i32,
#[serde(borrow)]
pub any_data: Value<'a>,
#[serde(borrow)]
pub properties: HashMap<&'a str, Value<'a>>,
}
impl<T> Type for EventBody<'_, T> {
fn signature() -> Signature<'static> {
<(&str, i32, i32, Value, HashMap<&str, Value>)>::signature()
}
}
#[derive(Debug, Serialize, Deserialize, Type)]
pub struct EventBodyQT {
#[serde(rename = "type")]
pub kind: String,
pub detail1: i32,
pub detail2: i32,
pub any_data: OwnedValue,
pub properties: (String, OwnedObjectPath),
}
#[derive(Clone, Debug, Serialize, Deserialize, Type)]
pub struct EventBodyOwned {
#[serde(rename = "type")]
pub kind: String,
pub detail1: i32,
pub detail2: i32,
pub any_data: OwnedValue,
pub properties: HashMap<String, OwnedValue>,
}
impl From<EventBodyQT> for EventBodyOwned {
fn from(body: EventBodyQT) -> Self {
let mut props = HashMap::new();
props.insert(
body.properties.0,
Value::ObjectPath(body.properties.1.into_inner()).to_owned(),
);
Self {
kind: body.kind,
detail1: body.detail1,
detail2: body.detail2,
any_data: body.any_data,
properties: props,
}
}
}
#[derive(Debug, Clone)]
#[non_exhaustive]
pub enum Event {
Interfaces(EventInterfaces),
Available(AvailableEvent),
Cache(CacheEvents),
Listener(EventListenerEvents),
}
#[derive(Debug, Clone)]
#[allow(clippy::module_name_repetitions)]
pub enum CacheEvents {
Add(AddAccessibleEvent),
Remove(RemoveAccessibleEvent),
}
#[derive(Debug, Clone, GenericEvent)]
#[try_from_zbus_message(body = "CacheItem")]
pub struct AddAccessibleEvent {
pub(crate) message: Arc<Message>,
pub(crate) body: CacheItem,
}
impl AddAccessibleEvent {
#[must_use]
pub fn item(&self) -> &CacheItem {
&self.body
}
#[must_use]
pub fn into_item(self) -> CacheItem {
self.body
}
}
#[derive(Debug, Clone, GenericEvent)]
#[try_from_zbus_message(body = "Accessible")]
pub struct RemoveAccessibleEvent {
pub(crate) message: Arc<Message>,
pub(crate) body: Accessible,
}
impl RemoveAccessibleEvent {
#[must_use]
pub fn as_accessible(&self) -> &Accessible {
&self.body
}
#[must_use]
pub fn into_accessible(self) -> Accessible {
self.body
}
}
#[derive(Debug, Clone, Serialize, Deserialize, Type)]
pub struct Accessible {
pub name: OwnedUniqueName,
pub path: OwnedObjectPath,
}
#[test]
fn test_accessible_signature() {
assert_eq!(Accessible::signature(), "(so)");
}
#[derive(Clone, Debug)]
#[non_exhaustive]
pub enum EventInterfaces {
Document(DocumentEvents),
Focus(FocusEvents),
Keyboard(KeyboardEvents),
Mouse(MouseEvents),
Object(ObjectEvents),
Terminal(TerminalEvents),
Window(WindowEvents),
}
impl TryFrom<AtspiEvent> for EventInterfaces {
type Error = AtspiError;
fn try_from(ev: AtspiEvent) -> Result<Self, Self::Error> {
let Some(interface) = ev.interface() else { return Err(AtspiError::MissingInterface); };
match interface.as_str() {
"org.a11y.atspi.Event.Document" => {
Ok(EventInterfaces::Document(DocumentEvents::try_from(ev)?))
}
"org.a11y.atspi.Event.Focus" => Ok(EventInterfaces::Focus(FocusEvents::try_from(ev)?)),
"org.a11y.atspi.Event.Keyboard" => {
Ok(EventInterfaces::Keyboard(KeyboardEvents::try_from(ev)?))
}
"org.a11y.atspi.Event.Mouse" => Ok(EventInterfaces::Mouse(MouseEvents::try_from(ev)?)),
"org.a11y.atspi.Event.Object" => {
Ok(EventInterfaces::Object(ObjectEvents::try_from(ev)?))
}
"org.a11y.atspi.Event.Terminal" => {
Ok(EventInterfaces::Terminal(TerminalEvents::try_from(ev)?))
}
"org.a11y.atspi.Event.Window" => {
Ok(EventInterfaces::Window(WindowEvents::try_from(ev)?))
}
_ => Err(AtspiError::UnknownInterface),
}
}
}
#[derive(Debug, Clone)]
pub struct AtspiEvent {
pub(crate) message: Arc<Message>,
pub(crate) body: EventBodyOwned,
}
impl<'name> PartialEq<AtspiEvent> for MemberName<'name> {
fn eq(&self, other: &AtspiEvent) -> bool {
let other_member = other.member().expect("AtspiEvent without member?");
*self == other_member
}
}
impl<'name> PartialEq<MemberName<'name>> for AtspiEvent {
fn eq(&self, other: &MemberName) -> bool {
let self_member = self.member().expect("AtspiEvent w/o member?");
self_member == *other
}
}
impl PartialEq for AtspiEvent {
fn eq(&self, other: &AtspiEvent) -> bool {
self.message.as_bytes() == other.message().as_bytes()
}
}
impl Eq for AtspiEvent {}
impl TryFrom<Arc<Message>> for AtspiEvent {
type Error = AtspiError;
fn try_from(message: Arc<Message>) -> Result<Self, Self::Error> {
let signature = message.body_signature()?;
let body = if signature == connection::QSPI_EVENT {
EventBodyOwned::from(message.body::<EventBodyQT>()?)
} else {
message.body::<EventBodyOwned>()?
};
Ok(Self { message, body })
}
}
#[derive(Debug, Clone, Serialize, Deserialize, Type)]
pub struct EventListener {
pub bus_name: OwnedUniqueName,
pub path: String,
}
#[test]
fn test_event_listener_signature() {
assert_eq!(EventListener::signature(), "(ss)");
}
#[derive(Clone, Debug)]
#[allow(clippy::module_name_repetitions)]
pub enum EventListenerEvents {
Registered(EventListenerRegisteredEvent),
Deregistered(EventListenerDeregisteredEvent),
}
#[derive(Clone, Debug, GenericEvent)]
#[try_from_zbus_message(body = "EventListener")]
pub struct EventListenerDeregisteredEvent {
pub(crate) message: Arc<Message>,
pub body: EventListener,
}
#[derive(Clone, Debug, GenericEvent)]
#[try_from_zbus_message(body = "EventListener")]
pub struct EventListenerRegisteredEvent {
pub(crate) message: Arc<Message>,
pub body: EventListener,
}
#[derive(Clone, Debug, GenericEvent)]
#[try_from_zbus_message(body = "Accessible")]
pub struct AvailableEvent {
pub(crate) message: Arc<Message>,
pub(crate) body: Accessible,
}
impl AvailableEvent {
#[must_use]
pub fn registry(&self) -> &Accessible {
&self.body
}
}
impl TryFrom<Arc<Message>> for Event {
type Error = AtspiError;
fn try_from(msg: Arc<Message>) -> Result<Event, AtspiError> {
let body_signature = msg.body_signature()?;
let message_signature = body_signature.as_str();
let signal_member = msg
.member()
.ok_or(AtspiError::MemberMatch("signal w/o member".to_string()))?;
let message_member = signal_member.as_str();
match message_signature {
"(so)" => match message_member {
"RemoveAccessible" => {
let ev = RemoveAccessibleEvent::try_from(msg)?;
Ok(Event::Cache(CacheEvents::Remove(ev)))
}
"Available" => {
let ev = AvailableEvent::try_from(msg)?;
Ok(Event::Available(ev))
}
_ => Err(AtspiError::UnknownSignal),
},
"siiva{sv}" | "siiv(so)" => {
let ev = AtspiEvent::try_from(msg)?;
let event_interfaces: EventInterfaces = ev.try_into()?;
Ok(Event::Interfaces(event_interfaces))
}
"(ss)" => {
if let Ok(ev) = EventListenerRegisteredEvent::try_from(msg.clone()) {
return Ok(Event::Listener(EventListenerEvents::Registered(ev)));
}
if let Ok(ev) = EventListenerDeregisteredEvent::try_from(msg) {
return Ok(Event::Listener(EventListenerEvents::Deregistered(ev)));
}
Err(AtspiError::UnknownSignal)
}
"((so)(so)(so)iiassusau)" => {
let ev = AddAccessibleEvent::try_from(msg)?;
Ok(Event::Cache(CacheEvents::Add(ev)))
}
_ => Err(AtspiError::UnknownBusSignature),
}
}
}
pub trait GenericEvent {
fn message(&self) -> &Arc<Message>;
fn interface(&self) -> Option<InterfaceName<'_>>;
fn member(&self) -> Option<MemberName<'_>>;
fn path(&self) -> Option<zvariant::ObjectPath<'_>>;
fn sender(&self) -> Result<Option<UniqueName>, AtspiError>;
}
impl AtspiEvent {
#[must_use]
pub fn body(&self) -> &EventBodyOwned {
&self.body
}
#[must_use]
pub fn event_string(&self) -> String {
let interface = self.message.interface().expect("Event should have an interface");
let interface = interface.rsplit('.').next().expect("Interface should contain a '.'");
let member = self.message.member().expect("Event should have a member");
let kind = self.kind();
format!("{interface}:{member}:{kind}")
}
#[must_use]
pub fn kind(&self) -> &str {
&self.body.kind
}
#[must_use]
pub fn detail1(&self) -> i32 {
self.body.detail1
}
#[must_use]
pub fn detail2(&self) -> i32 {
self.body.detail2
}
#[must_use]
pub fn any_data(&self) -> &zvariant::OwnedValue {
&self.body.any_data
}
#[must_use]
pub fn properties(&self) -> &HashMap<String, zvariant::OwnedValue> {
&self.body.properties
}
}
impl GenericEvent for AtspiEvent {
#[must_use]
fn message(&self) -> &Arc<Message> {
&self.message
}
#[must_use]
fn interface(&self) -> Option<InterfaceName<'_>> {
self.message.interface()
}
#[must_use]
fn member(&self) -> Option<MemberName<'_>> {
self.message.member()
}
#[must_use]
fn path(&self) -> std::option::Option<zbus::zvariant::ObjectPath<'_>> {
self.message.path()
}
fn sender(&self) -> Result<Option<zbus::names::UniqueName>, crate::AtspiError> {
Ok(self.message.header()?.sender()?.cloned())
}
}