1pub mod document;
2pub mod focus;
3pub mod keyboard;
4pub mod mouse;
5pub mod object;
6pub mod terminal;
7pub mod window;
8
9// Unmarshalled event body signatures: These outline the event specific deserialized event types.
10// Safety: These are evaluated at compile time.
11// ----
12// The signal signature "(so)" (an Accessible) is ambiguous, because it is used in:
13// - Cache : RemoveAccessible
14// - Socket: Available *( signals the availability of the `Registry` daemon.)
15//
16// ATSPI- and QSPI both describe the generic events. These can be converted into
17// specific signal types with TryFrom implementations. See crate::[`identify`]
18// EVENT_LISTENER_SIGNATURE is a type signature used to notify when events are registered or deregistered.
19// CACHE_ADD_SIGNATURE and *_REMOVE have very different types
20pub const ATSPI_EVENT_SIGNATURE: Signature<'_> =
21 Signature::from_static_str_unchecked(signature:"(siiva{sv})");
22pub const QSPI_EVENT_SIGNATURE: Signature<'_> = Signature::from_static_str_unchecked(signature:"(siiv(so))");
23pub const EVENT_LISTENER_SIGNATURE: Signature<'_> = Signature::from_static_str_unchecked(signature:"(ss)");
24pub const CACHE_ADD_SIGNATURE: Signature<'_> =
25 Signature::from_static_str_unchecked(signature:"((so)(so)(so)iiassusau)");
26
27use std::collections::HashMap;
28
29use serde::{Deserialize, Serialize};
30#[cfg(feature = "zbus")]
31use zbus::{MessageField, MessageFieldCode};
32use zbus_names::{OwnedUniqueName, UniqueName};
33use zvariant::{ObjectPath, OwnedObjectPath, OwnedValue, Signature, Type, Value};
34
35use crate::{
36 accessible::Accessible,
37 cache::{CacheItem, LegacyCacheItem},
38 events::{
39 document::DocumentEvents, focus::FocusEvents, keyboard::KeyboardEvents, mouse::MouseEvents,
40 object::ObjectEvents, terminal::TerminalEvents, window::WindowEvents,
41 },
42 AtspiError,
43};
44//use atspi_macros::try_from_zbus_message;
45
46#[must_use]
47pub fn signatures_are_eq(lhs: &Signature, rhs: &Signature) -> bool {
48 fn has_outer_parentheses(bytes: &[u8]) -> bool {
49 if let [b'(', inner @ .., b')'] = bytes {
50 inner.iter().fold(0, |count, byte| match byte {
51 b'(' => count + 1,
52 b')' if count != 0 => count - 1,
53 _ => count,
54 }) == 0
55 } else {
56 false
57 }
58 }
59
60 let bytes = lhs.as_bytes();
61 let lhs_sig_has_outer_parens = has_outer_parentheses(bytes);
62
63 let bytes = rhs.as_bytes();
64 let rhs_sig_has_outer_parens = has_outer_parentheses(bytes);
65
66 match (lhs_sig_has_outer_parens, rhs_sig_has_outer_parens) {
67 (true, false) => lhs.slice(1..lhs.len() - 1).as_bytes() == rhs.as_bytes(),
68 (false, true) => lhs.as_bytes() == rhs.slice(1..rhs.len() - 1).as_bytes(),
69 _ => lhs.as_bytes() == rhs.as_bytes(),
70 }
71}
72
73/// A borrowed body for events.
74#[derive(Debug, Serialize, Deserialize)]
75pub struct EventBody<'a, T> {
76 /// A generic "kind" type, defined by AT-SPI:
77 /// usually a `&'a str`, but can be another type like [`crate::state::State`].
78 #[serde(rename = "type")]
79 pub kind: T,
80 /// Generic first detail defined by AT-SPI.
81 pub detail1: i32,
82 /// Generic second detail defined by AT-SPI.
83 pub detail2: i32,
84 /// Generic "any_data" field defined in AT-SPI.
85 /// Can contain any type.
86 #[serde(borrow)]
87 pub any_data: Value<'a>,
88 /// Map of string to an any type.
89 /// This is not used for anything, but it is defined by AT-SPI.
90 #[serde(borrow)]
91 pub properties: HashMap<&'a str, Value<'a>>,
92}
93
94impl<T> Type for EventBody<'_, T> {
95 fn signature() -> Signature<'static> {
96 <(&str, i32, i32, Value, HashMap<&str, Value>)>::signature()
97 }
98}
99
100/// Qt event body, which is not the same as other GUI frameworks.
101/// Signature: "siiv(so)"
102#[derive(Debug, Serialize, Deserialize, Type)]
103pub struct EventBodyQT {
104 /// kind variant, used for specifying an event triple "object:state-changed:focused",
105 /// the "focus" part of this event is what is contained within the kind.
106 // #[serde(rename = "type")]
107 pub kind: String,
108 /// Generic detail1 value described by AT-SPI.
109 pub detail1: i32,
110 /// Generic detail2 value described by AT-SPI.
111 pub detail2: i32,
112 /// Generic any_data value described by AT-SPI.
113 /// This can be any type.
114 pub any_data: OwnedValue,
115 /// A tuple of properties.
116 /// Not in use.
117 pub properties: Accessible,
118}
119
120impl Default for EventBodyQT {
121 fn default() -> Self {
122 Self {
123 kind: String::new(),
124 detail1: 0,
125 detail2: 0,
126 any_data: Value::U8(0u8).into(),
127 properties: Accessible::default(),
128 }
129 }
130}
131
132/// Standard event body (GTK, `egui`, etc.)
133/// NOTE: Qt has its own signature: [`EventBodyQT`].
134/// Signature `(siiva{sv})`,
135#[derive(Clone, Debug, Serialize, Deserialize, Type, PartialEq)]
136pub struct EventBodyOwned {
137 /// kind variant, used for specifying an event triple "object:state-changed:focused",
138 /// the "focus" part of this event is what is contained within the kind.
139 #[serde(rename = "type")]
140 pub kind: String,
141 /// Generic detail1 value described by AT-SPI.
142 pub detail1: i32,
143 /// Generic detail2 value described by AT-SPI.
144 pub detail2: i32,
145 /// Generic any_data value described by AT-SPI.
146 /// This can be any type.
147 pub any_data: OwnedValue,
148 /// A map of properties.
149 /// Not in use.
150 pub properties: HashMap<String, OwnedValue>,
151}
152
153impl From<EventBodyQT> for EventBodyOwned {
154 fn from(body: EventBodyQT) -> Self {
155 let accessible: Accessible = Accessible { name: body.properties.name, path: body.properties.path };
156 let mut props: HashMap = HashMap::new();
157 props.insert(k:accessible.name, v:Value::ObjectPath(accessible.path.into()).to_owned());
158 Self {
159 kind: body.kind,
160 detail1: body.detail1,
161 detail2: body.detail2,
162 any_data: body.any_data,
163 properties: props,
164 }
165 }
166}
167
168impl Default for EventBodyOwned {
169 fn default() -> Self {
170 Self {
171 kind: String::new(),
172 detail1: 0,
173 detail2: 0,
174 any_data: Value::U8(0u8).into(),
175 properties: HashMap::new(),
176 }
177 }
178}
179
180/// Encapsulates the various different accessibility bus signal types.
181///
182/// Assumes being non exhaustive to allow for future- or custom signals.
183#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
184#[non_exhaustive]
185pub enum Event {
186 /// See: [`DocumentEvents`].
187 Document(DocumentEvents),
188 /// See: [`FocusEvents`].
189 Focus(FocusEvents),
190 /// See: [`KeyboardEvents`].
191 Keyboard(KeyboardEvents),
192 /// See: [`MouseEvents`].
193 Mouse(MouseEvents),
194 /// See: [`ObjectEvents`].
195 Object(ObjectEvents),
196 /// See: [`TerminalEvents`].
197 Terminal(TerminalEvents),
198 /// See: [`WindowEvents`].
199 Window(WindowEvents),
200 /// See: [`AvailableEvent`].
201 Available(AvailableEvent),
202 /// See: [`CacheEvents`].
203 Cache(CacheEvents),
204 /// See: [`EventListenerEvents`].
205 Listener(EventListenerEvents),
206}
207
208impl HasMatchRule for CacheEvents {
209 const MATCH_RULE_STRING: &'static str = "type='signal',interface='org.a11y.atspi.Event.Cache'";
210}
211
212impl HasRegistryEventString for CacheEvents {
213 const REGISTRY_EVENT_STRING: &'static str = "Cache";
214}
215
216impl HasMatchRule for EventListenerEvents {
217 const MATCH_RULE_STRING: &'static str =
218 "type='signal',interface='org.a11y.atspi.Event.Registry'";
219}
220
221impl HasRegistryEventString for EventListenerEvents {
222 const REGISTRY_EVENT_STRING: &'static str = "Event";
223}
224
225/// All events related to the `org.a11y.atspi.Cache` interface.
226/// Note that these are not telling the client that an item *has been added* to a cache.
227/// It is telling the client "here is a bunch of information to store it in your cache".
228#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Eq, Hash)]
229#[allow(clippy::module_name_repetitions)]
230pub enum CacheEvents {
231 /// See: [`AddAccessibleEvent`].
232 Add(AddAccessibleEvent),
233 /// See: [`LegacyAddAccessibleEvent`].
234 LegacyAdd(LegacyAddAccessibleEvent),
235 /// See: [`RemoveAccessibleEvent`].
236 Remove(RemoveAccessibleEvent),
237}
238
239/// Type that contains the `zbus::Message` for meta information and
240/// the [`crate::cache::LegacyCacheItem`]
241#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default, Eq, Hash)]
242pub struct LegacyAddAccessibleEvent {
243 /// The [`Accessible`] the event applies to.
244 pub item: Accessible,
245 /// A cache item to add to the internal cache.
246 pub node_added: LegacyCacheItem,
247}
248
249impl_from_user_facing_event_for_interface_event_enum!(
250 LegacyAddAccessibleEvent,
251 CacheEvents,
252 CacheEvents::LegacyAdd
253);
254impl_from_user_facing_type_for_event_enum!(LegacyAddAccessibleEvent, Event::Cache);
255impl_try_from_event_for_user_facing_type!(
256 LegacyAddAccessibleEvent,
257 CacheEvents::LegacyAdd,
258 Event::Cache
259);
260event_test_cases!(LegacyAddAccessibleEvent);
261impl_from_dbus_message!(LegacyAddAccessibleEvent);
262impl_to_dbus_message!(LegacyAddAccessibleEvent);
263
264impl GenericEvent<'_> for LegacyAddAccessibleEvent {
265 const REGISTRY_EVENT_STRING: &'static str = "Cache:Add";
266 const MATCH_RULE_STRING: &'static str =
267 "type='signal',interface='org.a11y.atspi.Cache',member='AddAccessible'";
268 const DBUS_MEMBER: &'static str = "AddAccessible";
269 const DBUS_INTERFACE: &'static str = "org.a11y.atspi.Cache";
270
271 type Body = LegacyCacheItem;
272
273 fn build(item: Accessible, body: Self::Body) -> Result<Self, AtspiError> {
274 Ok(Self { item, node_added: body })
275 }
276
277 fn sender(&self) -> String {
278 self.item.name.clone()
279 }
280 fn path(&self) -> ObjectPath<'_> {
281 self.item.path.clone().into()
282 }
283 fn body(&self) -> Self::Body {
284 self.node_added.clone()
285 }
286}
287
288/// Type that contains the `zbus::Message` for meta information and
289/// the [`crate::cache::CacheItem`]
290#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default, Eq, Hash)]
291pub struct AddAccessibleEvent {
292 /// The [`Accessible`] the event applies to.
293 pub item: Accessible,
294 /// A cache item to add to the internal cache.
295 pub node_added: CacheItem,
296}
297
298impl_from_user_facing_event_for_interface_event_enum!(
299 AddAccessibleEvent,
300 CacheEvents,
301 CacheEvents::Add
302);
303impl_from_user_facing_type_for_event_enum!(AddAccessibleEvent, Event::Cache);
304impl_try_from_event_for_user_facing_type!(AddAccessibleEvent, CacheEvents::Add, Event::Cache);
305event_test_cases!(AddAccessibleEvent);
306
307impl GenericEvent<'_> for AddAccessibleEvent {
308 const REGISTRY_EVENT_STRING: &'static str = "Cache:Add";
309 const MATCH_RULE_STRING: &'static str =
310 "type='signal',interface='org.a11y.atspi.Cache',member='AddAccessible'";
311 const DBUS_MEMBER: &'static str = "AddAccessible";
312 const DBUS_INTERFACE: &'static str = "org.a11y.atspi.Cache";
313
314 type Body = CacheItem;
315
316 fn build(item: Accessible, body: Self::Body) -> Result<Self, AtspiError> {
317 Ok(Self { item, node_added: body })
318 }
319
320 fn sender(&self) -> String {
321 self.item.name.clone()
322 }
323 fn path(&self) -> ObjectPath<'_> {
324 self.item.path.clone().into()
325 }
326 fn body(&self) -> Self::Body {
327 self.node_added.clone()
328 }
329}
330impl<'a, T: GenericEvent<'a>> HasMatchRule for T {
331 const MATCH_RULE_STRING: &'static str = <T as GenericEvent>::MATCH_RULE_STRING;
332}
333impl<'a, T: GenericEvent<'a>> HasRegistryEventString for T {
334 const REGISTRY_EVENT_STRING: &'static str = <T as GenericEvent>::REGISTRY_EVENT_STRING;
335}
336impl_from_dbus_message!(AddAccessibleEvent);
337impl_to_dbus_message!(AddAccessibleEvent);
338
339/// `Cache::RemoveAccessible` signal event type.
340#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default, Eq, Hash)]
341pub struct RemoveAccessibleEvent {
342 /// The application that emitted the signal TODO Check Me
343 /// The [`Accessible`] the event applies to.
344 pub item: Accessible,
345 /// The node that was removed from the application tree TODO Check Me
346 pub node_removed: Accessible,
347}
348
349impl_from_user_facing_event_for_interface_event_enum!(
350 RemoveAccessibleEvent,
351 CacheEvents,
352 CacheEvents::Remove
353);
354impl_from_user_facing_type_for_event_enum!(RemoveAccessibleEvent, Event::Cache);
355impl_try_from_event_for_user_facing_type!(RemoveAccessibleEvent, CacheEvents::Remove, Event::Cache);
356event_test_cases!(RemoveAccessibleEvent);
357impl GenericEvent<'_> for RemoveAccessibleEvent {
358 const REGISTRY_EVENT_STRING: &'static str = "Cache:Remove";
359 const MATCH_RULE_STRING: &'static str =
360 "type='signal',interface='org.a11y.atspi.Cache',member='RemoveAccessible'";
361 const DBUS_MEMBER: &'static str = "RemoveAccessible";
362 const DBUS_INTERFACE: &'static str = "org.a11y.atspi.Cache";
363
364 type Body = Accessible;
365
366 fn build(item: Accessible, body: Self::Body) -> Result<Self, AtspiError> {
367 Ok(Self { item, node_removed: body })
368 }
369 fn sender(&self) -> String {
370 self.item.name.clone()
371 }
372 fn path(&self) -> ObjectPath<'_> {
373 self.item.path.clone().into()
374 }
375 fn body(&self) -> Self::Body {
376 self.node_removed.clone()
377 }
378}
379
380impl_from_dbus_message!(RemoveAccessibleEvent);
381impl_to_dbus_message!(RemoveAccessibleEvent);
382
383#[cfg(test)]
384pub mod accessible_deserialization_tests {
385 use crate::events::Accessible;
386 use zvariant::Value;
387
388 #[test]
389 fn try_into_value() {
390 let acc = Accessible::default();
391 let value_struct = Value::try_from(acc).expect("Unable to convert into a zvariant::Value");
392 let Value::Structure(structure) = value_struct else {
393 panic!("Unable to destructure a structure out of the Value.");
394 };
395 let vals = structure.into_fields();
396 assert_eq!(vals.len(), 2);
397 let Value::Str(bus_name) = vals.get(0).unwrap() else {
398 panic!("Unable to destructure field value: {:?}", vals.get(0).unwrap());
399 };
400 assert_eq!(bus_name, ":0.0");
401 let Value::ObjectPath(path) = vals.get(1).unwrap() else {
402 panic!("Unable to destructure field value: {:?}", vals.get(1).unwrap());
403 };
404 assert_eq!(path.as_str(), "/org/a11y/atspi/accessible/null");
405 }
406 #[test]
407 fn try_from_value() {}
408}
409
410#[cfg(test)]
411pub mod accessible_tests {
412 use super::Accessible;
413
414 #[test]
415 fn test_accessible_default_doesnt_panic() {
416 let acc = Accessible::default();
417 assert_eq!(acc.name.as_str(), ":0.0");
418 assert_eq!(acc.path.as_str(), "/org/a11y/atspi/accessible/null");
419 }
420}
421#[cfg(feature = "zbus")]
422impl TryFrom<&zbus::Message> for Accessible {
423 type Error = AtspiError;
424 fn try_from(message: &zbus::Message) -> Result<Self, Self::Error> {
425 let path: ObjectPath<'_> = message.path().expect(msg:"returned path is either Some or panics");
426 let owned_path: OwnedObjectPath = OwnedObjectPath::try_from(path)?;
427 let fields: MessageFields<'_> = message.fields()?;
428 let sender: Option<&MessageField<'_>> = fields.get_field(code:MessageFieldCode::Sender);
429 let sender: &MessageField<'_> = sender
430 .expect(msg:"We get the sender field from a valid MessageFieldCode, so it should be there");
431
432 let MessageField::Sender(unique_name: &UniqueName<'_>) = sender else {
433 return Err(AtspiError::Conversion("Unable to convert zbus::Message to Accessible"));
434 };
435 let name_string: String = unique_name.as_str().to_owned();
436
437 Ok(Accessible { name: name_string, path: owned_path })
438 }
439}
440
441#[cfg(feature = "zbus")]
442impl TryFrom<&zbus::Message> for EventBodyOwned {
443 type Error = AtspiError;
444
445 fn try_from(message: &zbus::Message) -> Result<Self, Self::Error> {
446 let signature: Signature<'_> = message.body_signature()?;
447 if signatures_are_eq(&signature, &QSPI_EVENT_SIGNATURE) {
448 Ok(EventBodyOwned::from(message.body::<EventBodyQT>()?))
449 } else if signatures_are_eq(&signature, &ATSPI_EVENT_SIGNATURE) {
450 Ok(message.body::<EventBodyOwned>()?)
451 } else {
452 Err(AtspiError::Conversion(
453 "Unable to convert from zbus::Message to EventBodyQT or EventBodyOwned",
454 ))
455 }
456 }
457}
458
459/// Signal type emitted by `EventListenerRegistered` and `EventListenerDeregistered` signals,
460/// which belong to the `Registry` interface, implemented by the registry-daemon.
461#[derive(Debug, Clone, Serialize, Deserialize, Type, PartialEq, Eq, Hash)]
462pub struct EventListeners {
463 pub bus_name: OwnedUniqueName,
464 pub path: String,
465}
466impl Default for EventListeners {
467 fn default() -> Self {
468 Self {
469 bus_name: UniqueName::try_from(":0.0").unwrap().into(),
470 path: "/org/a11y/atspi/accessible/null".to_string(),
471 }
472 }
473}
474#[test]
475fn test_event_listener_default_no_panic() {
476 let el: EventListeners = EventListeners::default();
477 assert_eq!(el.bus_name.as_str(), ":0.0");
478 assert_eq!(el.path.as_str(), "/org/a11y/atspi/accessible/null");
479}
480
481#[test]
482fn test_event_listener_signature() {
483 assert_eq_signatures!(&EventListeners::signature(), &EVENT_LISTENER_SIGNATURE);
484}
485
486/// Covers both `EventListener` events.
487#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
488#[allow(clippy::module_name_repetitions)]
489pub enum EventListenerEvents {
490 /// See: [`EventListenerRegisteredEvent`].
491 Registered(EventListenerRegisteredEvent),
492 /// See: [`EventListenerDeregisteredEvent`].
493 Deregistered(EventListenerDeregisteredEvent),
494}
495
496/// An event that is emitted by the registry daemon, to inform that an event has been deregistered
497/// to no longer listen for.
498#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default, Eq, Hash)]
499pub struct EventListenerDeregisteredEvent {
500 /// The [`Accessible`] the event applies to.
501 pub item: Accessible,
502 /// A list of events that have been deregistered via the registry interface.
503 /// See `atspi-connection`.
504 pub deregistered_event: EventListeners,
505}
506
507impl_from_user_facing_event_for_interface_event_enum!(
508 EventListenerDeregisteredEvent,
509 EventListenerEvents,
510 EventListenerEvents::Deregistered
511);
512impl_from_user_facing_type_for_event_enum!(EventListenerDeregisteredEvent, Event::Listener);
513impl_try_from_event_for_user_facing_type!(
514 EventListenerDeregisteredEvent,
515 EventListenerEvents::Deregistered,
516 Event::Listener
517);
518event_test_cases!(EventListenerDeregisteredEvent);
519impl GenericEvent<'_> for EventListenerDeregisteredEvent {
520 const REGISTRY_EVENT_STRING: &'static str = "Registry:EventListenerDeregistered";
521 const MATCH_RULE_STRING: &'static str =
522 "type='signal',interface='org.a11y.atspi.Registry',member='EventListenerDeregistered'";
523 const DBUS_MEMBER: &'static str = "EventListenerDeregistered";
524 const DBUS_INTERFACE: &'static str = "org.a11y.atspi.Registry";
525
526 type Body = EventListeners;
527
528 fn build(item: Accessible, body: Self::Body) -> Result<Self, AtspiError> {
529 Ok(Self { item, deregistered_event: body })
530 }
531 fn sender(&self) -> String {
532 self.item.name.clone()
533 }
534 fn path(&self) -> ObjectPath<'_> {
535 self.item.path.clone().into()
536 }
537 fn body(&self) -> Self::Body {
538 self.deregistered_event.clone()
539 }
540}
541impl_from_dbus_message!(EventListenerDeregisteredEvent);
542impl_to_dbus_message!(EventListenerDeregisteredEvent);
543
544/// An event that is emitted by the regostry daemon to signal that an event has been registered to listen for.
545#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default, Eq, Hash)]
546pub struct EventListenerRegisteredEvent {
547 /// The [`Accessible`] the event applies to.
548 pub item: Accessible,
549 /// A list of events that have been registered via the registry interface.
550 /// See `atspi-connection`.
551 pub registered_event: EventListeners,
552}
553
554impl_from_user_facing_event_for_interface_event_enum!(
555 EventListenerRegisteredEvent,
556 EventListenerEvents,
557 EventListenerEvents::Registered
558);
559impl_from_user_facing_type_for_event_enum!(EventListenerRegisteredEvent, Event::Listener);
560impl_try_from_event_for_user_facing_type!(
561 EventListenerRegisteredEvent,
562 EventListenerEvents::Registered,
563 Event::Listener
564);
565event_test_cases!(EventListenerRegisteredEvent);
566impl GenericEvent<'_> for EventListenerRegisteredEvent {
567 const REGISTRY_EVENT_STRING: &'static str = "Registry:EventListenerRegistered";
568 const MATCH_RULE_STRING: &'static str =
569 "type='signal',interface='org.a11y.atspi.Registry',member='EventListenerRegistered'";
570 const DBUS_MEMBER: &'static str = "EventListenerRegistered";
571 const DBUS_INTERFACE: &'static str = "org.a11y.atspi.Registry";
572
573 type Body = EventListeners;
574
575 fn build(item: Accessible, body: Self::Body) -> Result<Self, AtspiError> {
576 Ok(Self { item, registered_event: body })
577 }
578 fn sender(&self) -> String {
579 self.item.name.clone()
580 }
581 fn path(&self) -> ObjectPath<'_> {
582 self.item.path.clone().into()
583 }
584 fn body(&self) -> Self::Body {
585 self.registered_event.clone()
586 }
587}
588impl_from_dbus_message!(EventListenerRegisteredEvent);
589impl_to_dbus_message!(EventListenerRegisteredEvent);
590
591/// An event that is emitted when the registry daemon has started.
592#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default, Eq, Hash)]
593pub struct AvailableEvent {
594 /// The [`Accessible`] the event applies to.
595 pub item: Accessible,
596 pub socket: Accessible,
597}
598impl From<AvailableEvent> for Event {
599 fn from(ev: AvailableEvent) -> Event {
600 Event::Available(ev)
601 }
602}
603impl TryFrom<Event> for AvailableEvent {
604 type Error = AtspiError;
605 fn try_from(generic_event: Event) -> Result<AvailableEvent, Self::Error> {
606 if let Event::Available(specific_event: AvailableEvent) = generic_event {
607 Ok(specific_event)
608 } else {
609 Err(AtspiError::Conversion("Invalid type"))
610 }
611 }
612}
613event_test_cases!(AvailableEvent);
614impl GenericEvent<'_> for AvailableEvent {
615 const REGISTRY_EVENT_STRING: &'static str = "Socket:Available";
616 const MATCH_RULE_STRING: &'static str =
617 "type='signal',interface='org.a11y.atspi.Socket',member='Available'";
618 const DBUS_MEMBER: &'static str = "Available";
619 const DBUS_INTERFACE: &'static str = "org.a11y.atspi.Socket";
620
621 type Body = Accessible;
622
623 fn build(item: Accessible, body: Self::Body) -> Result<Self, AtspiError> {
624 Ok(Self { item, socket: body })
625 }
626 fn sender(&self) -> String {
627 self.item.name.clone()
628 }
629 fn path(&self) -> ObjectPath<'_> {
630 self.item.path.clone().into()
631 }
632 fn body(&self) -> Self::Body {
633 self.socket.clone()
634 }
635}
636impl_from_dbus_message!(AvailableEvent);
637impl_to_dbus_message!(AvailableEvent);
638
639#[cfg(feature = "zbus")]
640impl TryFrom<&zbus::Message> for Event {
641 type Error = AtspiError;
642
643 fn try_from(msg: &zbus::Message) -> Result<Event, AtspiError> {
644 let body_signature = msg.body_signature()?;
645 let body_signature = body_signature.as_str();
646 let signal_member = msg.member().ok_or(AtspiError::MissingMember)?;
647 let member_str = signal_member.as_str();
648 let Some(interface) = msg.interface() else {
649 return Err(AtspiError::MissingInterface);
650 };
651
652 // As we are matching against `body_signature()`, which yields the marshalled D-Bus signatures,
653 // we do not expect outer parentheses.
654 // However, `Cache` signals are often emitted with an outer parentheses, so we also try to
655 // match against the same signature, but with outer parentheses.
656 match (interface.as_str(), member_str, body_signature) {
657 ("org.a11y.atspi.Socket", "Available", "so") => {
658 Ok(AvailableEvent::try_from(msg)?.into())
659 }
660 ("org.a11y.atspi.Event.Object", _, "siiva{sv}" | "siiv(so)") => {
661 Ok(Event::Object(ObjectEvents::try_from(msg)?))
662 }
663 ("org.a11y.atspi.Event.Document", _, "siiva{sv}" | "siiv(so)") => {
664 Ok(Event::Document(DocumentEvents::try_from(msg)?))
665 }
666 ("org.a11y.atspi.Event.Window", _, "siiva{sv}" | "siiv(so)") => {
667 Ok(Event::Window(WindowEvents::try_from(msg)?))
668 }
669 ("org.a11y.atspi.Event.Terminal", _, "siiva{sv}" | "siiv(so)") => {
670 Ok(Event::Terminal(TerminalEvents::try_from(msg)?))
671 }
672 ("org.a11y.atspi.Event.Mouse", _, "siiva{sv}" | "siiv(so)") => {
673 Ok(Event::Mouse(MouseEvents::try_from(msg)?))
674 }
675 ("org.a11y.atspi.Event.Focus", _, "siiva{sv}" | "siiv(so)") => {
676 Ok(Event::Focus(FocusEvents::try_from(msg)?))
677 }
678 ("org.a11y.atspi.Event.Keyboard", _, "siiva{sv}" | "siiv(so)") => {
679 Ok(Event::Keyboard(KeyboardEvents::try_from(msg)?))
680 }
681 ("org.a11y.atspi.Registry", "EventListenerRegistered", "ss") => {
682 Ok(EventListenerRegisteredEvent::try_from(msg)?.into())
683 }
684 ("org.a11y.atspi.Registry", "EventListenerDeregistered", "ss") => {
685 Ok(EventListenerDeregisteredEvent::try_from(msg)?.into())
686 }
687 (
688 "org.a11y.atspi.Cache",
689 "AddAccessible",
690 "(so)(so)(so)iiassusau" | "((so)(so)(so)iiassusau)",
691 ) => Ok(AddAccessibleEvent::try_from(msg)?.into()),
692 (
693 "org.a11y.atspi.Cache",
694 "AddAccessible",
695 "(so)(so)(so)a(so)assusau" | "((so)(so)(so)a(so)assusau)",
696 ) => Ok(LegacyAddAccessibleEvent::try_from(msg)?.into()),
697 ("org.a11y.atspi.Cache", "RemoveAccessible", "so" | "(so)") => {
698 Ok(RemoveAccessibleEvent::try_from(msg)?.into())
699 }
700 (_iface, _method, sig) => Err(AtspiError::UnknownBusSignature(sig.to_string())),
701 }
702 }
703}
704
705/// Shared behavior of bus `Signal` events.
706pub trait GenericEvent<'a> {
707 /// The `DBus` member for the event.
708 /// For example, for an [`object::TextChangedEvent`] this should be `"TextChanged"`
709 const DBUS_MEMBER: &'static str;
710 /// The `DBus` interface name for this event.
711 /// For example, for any event within [`object`], this should be "org.a11y.atspi.Event.Object".
712 const DBUS_INTERFACE: &'static str;
713 /// A static match rule string for `DBus`.
714 /// This should usually be a string that looks like this: `"type='signal',interface='org.a11y.atspi.Event.Object',member='PropertyChange'"`;
715 /// This should be deprecated in favour of composing the string from [`Self::DBUS_MEMBER`] and [`Self::DBUS_INTERFACE`].
716 const MATCH_RULE_STRING: &'static str;
717 /// A registry event string for registering for event receiving via the `RegistryProxy`.
718 /// This should be deprecated in favour of composing the string from [`Self::DBUS_MEMBER`] and [`Self::DBUS_INTERFACE`].
719 const REGISTRY_EVENT_STRING: &'static str;
720
721 /// What is the body type of this event.
722 type Body: Type + Serialize + Deserialize<'a>;
723
724 /// Build the event from the object pair (Accessible and the Body).
725 ///
726 /// # Errors
727 ///
728 /// When the body type, which is what the raw message looks like over `DBus`, does not match the type that is expected for the given event.
729 /// It is not possible for this to error on most events, but on events whose raw message [`Self::Body`] type contains a [`enum@zvariant::Value`], you may get errors when constructing the structure.
730 fn build(item: Accessible, body: Self::Body) -> Result<Self, AtspiError>
731 where
732 Self: Sized;
733
734 /// Path of the signalling object.
735 fn path(&self) -> ObjectPath<'_>;
736
737 /// Sender of the signal.
738 ///
739 /// ### Errors
740 /// - when deserializing the header failed, or
741 /// - When `zbus::get_field!` finds that 'sender' is an invalid field.
742 fn sender(&self) -> String;
743
744 /// The body of the object.
745 fn body(&self) -> Self::Body;
746}
747
748/// A specific trait *only* to define match rules.
749pub trait HasMatchRule {
750 /// A static match rule string for `DBus`.
751 /// This should usually be a string that looks like this: `"type='signal',interface='org.a11y.atspi.Event.Object',member='PropertyChange'"`;
752 /// This should be deprecated in favour of composing the string from [`GenericEvent::DBUS_MEMBER`] and [`GenericEvent::DBUS_INTERFACE`].
753 const MATCH_RULE_STRING: &'static str;
754}
755
756/// A specific trait *only* to define registry event matches.
757pub trait HasRegistryEventString {
758 /// A registry event string for registering for event receiving via the `RegistryProxy`.
759 /// This should be deprecated in favour of composing the string from [`GenericEvent::DBUS_MEMBER`] and [`GenericEvent::DBUS_INTERFACE`].
760 const REGISTRY_EVENT_STRING: &'static str;
761}
762
763#[cfg(test)]
764mod tests {
765 use super::{
766 signatures_are_eq, EventBodyOwned, EventBodyQT, ATSPI_EVENT_SIGNATURE, QSPI_EVENT_SIGNATURE,
767 };
768 use std::collections::HashMap;
769 use zvariant::{ObjectPath, Signature, Type};
770
771 #[test]
772 fn check_event_body_qt_signature() {
773 assert_eq_signatures!(&<EventBodyQT as Type>::signature(), &QSPI_EVENT_SIGNATURE);
774 }
775
776 #[test]
777 fn check_event_body_signature() {
778 assert_eq_signatures!(&<EventBodyOwned as Type>::signature(), &ATSPI_EVENT_SIGNATURE);
779 }
780
781 #[test]
782 fn test_event_body_qt_to_event_body_owned_conversion() {
783 let event_body: EventBodyOwned = EventBodyQT::default().into();
784
785 let accessible = crate::Accessible::default();
786 let name = accessible.name;
787 let path = accessible.path;
788 let props = HashMap::from([(name, ObjectPath::try_from(path).unwrap().into())]);
789 assert_eq!(event_body.properties, props);
790 }
791
792 // `assert_eq_signatures!` and `signatures_are_eq` are helpers to deal with the difference
793 // in `Signatures` as consequence of marshalling. While `zvariant` is very lenient with respect
794 // to outer parentheses, these helpers only take one marshalling step into account.
795 #[test]
796 fn test_signatures_are_equal_macro_and_fn() {
797 let with_parentheses = &Signature::from_static_str_unchecked("(ii)");
798 let without_parentheses = &Signature::from_static_str_unchecked("ii");
799 assert_eq_signatures!(with_parentheses, without_parentheses);
800 assert!(signatures_are_eq(with_parentheses, without_parentheses));
801 // test against themselves
802 assert!(signatures_are_eq(with_parentheses, with_parentheses));
803 assert!(signatures_are_eq(without_parentheses, without_parentheses));
804 assert!(signatures_are_eq(with_parentheses, with_parentheses));
805 assert!(signatures_are_eq(without_parentheses, without_parentheses));
806 let with_parentheses = &Signature::from_static_str_unchecked("(ii)(ii)");
807 let without_parentheses = &Signature::from_static_str_unchecked("((ii)(ii))");
808 assert_eq_signatures!(with_parentheses, without_parentheses);
809 assert!(signatures_are_eq(with_parentheses, without_parentheses));
810 // test against themselves
811 assert!(signatures_are_eq(with_parentheses, with_parentheses));
812 assert!(signatures_are_eq(without_parentheses, without_parentheses));
813 assert_eq_signatures!(with_parentheses, with_parentheses);
814 assert_eq_signatures!(without_parentheses, without_parentheses);
815 // test false cases with unbalanced parentheses
816 let with_parentheses = &Signature::from_static_str_unchecked("(ii)(ii)");
817 let without_parentheses = &Signature::from_static_str_unchecked("((ii)(ii)");
818 assert!(!signatures_are_eq(with_parentheses, without_parentheses));
819 // test case with more than one extra outer parentheses
820 let with_parentheses = &Signature::from_static_str_unchecked("((ii)(ii))");
821 let without_parentheses = &Signature::from_static_str_unchecked("((((ii)(ii))))");
822 assert!(!signatures_are_eq(with_parentheses, without_parentheses));
823 }
824}
825