atspi_common/events/
mod.rs

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("(siiva{sv})");
22pub const QSPI_EVENT_SIGNATURE: Signature<'_> = Signature::from_static_str_unchecked("(siiv(so))");
23pub const EVENT_LISTENER_SIGNATURE: Signature<'_> = Signature::from_static_str_unchecked("(ss)");
24pub const CACHE_ADD_SIGNATURE: Signature<'_> =
25	Signature::from_static_str_unchecked("((so)(so)(so)iiassusau)");
26
27use std::collections::HashMap;
28
29use serde::{Deserialize, Serialize};
30use zbus_lockstep_macros::validate;
31use zbus_names::{OwnedUniqueName, UniqueName};
32#[cfg(feature = "zbus")]
33use zvariant::OwnedObjectPath;
34use zvariant::{ObjectPath, OwnedValue, Signature, Type, Value};
35
36use crate::{
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, ObjectRef,
43};
44
45/// A borrowed body for events.
46#[derive(Debug, Serialize, Deserialize)]
47pub struct EventBody<'a, T> {
48	/// A generic "kind" type, defined by AT-SPI:
49	/// usually a `&'a str`, but can be another type like [`crate::state::State`].
50	#[serde(rename = "type")]
51	pub kind: T,
52	/// Generic first detail defined by AT-SPI.
53	pub detail1: i32,
54	/// Generic second detail defined by AT-SPI.
55	pub detail2: i32,
56	/// Generic `any_data` field defined in AT-SPI.
57	/// Can contain any type.
58	#[serde(borrow)]
59	pub any_data: Value<'a>,
60	/// Map of string to an any type.
61	/// This is not used for anything, but it is defined by AT-SPI.
62	#[serde(borrow)]
63	pub properties: HashMap<UniqueName<'a>, Value<'a>>,
64}
65
66impl<T> Type for EventBody<'_, T> {
67	fn signature() -> Signature<'static> {
68		<(&str, i32, i32, Value, HashMap<&str, Value>)>::signature()
69	}
70}
71
72/// Qt event body, which is not the same as other GUI frameworks.
73/// Signature:  "siiv(so)"
74#[derive(Debug, Serialize, Deserialize, Type)]
75pub struct EventBodyQT {
76	/// kind variant, used for specifying an event triple "object:state-changed:focused",
77	/// the "focus" part of this event is what is contained within the kind.
78	// #[serde(rename = "type")]
79	pub kind: String,
80	/// Generic detail1 value described by AT-SPI.
81	pub detail1: i32,
82	/// Generic detail2 value described by AT-SPI.
83	pub detail2: i32,
84	/// Generic `any_data` value described by AT-SPI.
85	/// This can be any type.
86	pub any_data: OwnedValue,
87	/// A tuple of properties.
88	/// Not in use.
89	pub properties: ObjectRef,
90}
91
92impl Default for EventBodyQT {
93	fn default() -> Self {
94		Self {
95			kind: String::new(),
96			detail1: 0,
97			detail2: 0,
98			any_data: 0u8.into(),
99			properties: ObjectRef::default(),
100		}
101	}
102}
103
104/// Standard event body (GTK, `egui`, etc.)
105/// NOTE: Qt has its own signature: [`EventBodyQT`].
106/// Signature `(siiva{sv})`,
107#[validate(signal: "PropertyChange")]
108#[derive(Debug, Serialize, Deserialize, Type, PartialEq)]
109pub struct EventBodyOwned {
110	/// kind variant, used for specifying an event triple "object:state-changed:focused",
111	/// the "focus" part of this event is what is contained within the kind.
112	#[serde(rename = "type")]
113	pub kind: String,
114	/// Generic detail1 value described by AT-SPI.
115	pub detail1: i32,
116	/// Generic detail2 value described by AT-SPI.
117	pub detail2: i32,
118	/// Generic `any_data` value described by AT-SPI.
119	/// This can be any type.
120	pub any_data: OwnedValue,
121	/// A map of properties.
122	/// Not in use.
123	pub properties: HashMap<OwnedUniqueName, OwnedValue>,
124}
125
126impl From<EventBodyQT> for EventBodyOwned {
127	fn from(body: EventBodyQT) -> Self {
128		let mut props = HashMap::new();
129
130		let name = body.properties.name;
131		let path = body.properties.path;
132
133		// We know `path` is a `OwnedObjectPath`, so the conversion to
134		// `OwnedValue` is infallible at present.
135		// Should this ever change, we need to know.
136		let value = Value::ObjectPath(path.into()).try_to_owned().unwrap_or_else(|err| {
137			panic!("Error occurred: {err:?}");
138		});
139
140		props.insert(name, value);
141		Self {
142			kind: body.kind,
143			detail1: body.detail1,
144			detail2: body.detail2,
145			any_data: body.any_data,
146			properties: props,
147		}
148	}
149}
150
151impl Default for EventBodyOwned {
152	fn default() -> Self {
153		Self {
154			kind: String::new(),
155			detail1: 0,
156			detail2: 0,
157			any_data: 0u8.into(),
158			properties: HashMap::new(),
159		}
160	}
161}
162
163/// Safety: This implementation of [`Clone`] *can panic!* Although the chance is extremely remote.
164///
165/// If:
166/// 1. the `any_data` or `properties` field contain an [`std::os::fd::OwnedFd`] type, and
167/// 2. the maximum number of open files for the process is exceeded.
168///
169/// Then, and only then, will this function panic.
170/// None of the types in [`crate::events`] use [`std::os::fd::OwnedFd`].
171/// Events on the AT-SPI bus *could, theoretically* send a file descriptor, but nothing in the
172/// specification allows that.
173///
174/// See [`zvariant::Value::try_clone`] for more information.
175impl Clone for EventBodyOwned {
176	fn clone(&self) -> Self {
177		let cloned_any_data = self.any_data.try_clone().unwrap_or_else(|err| {
178			panic!("Failure cloning 'any_data' field: {err:?}");
179		});
180
181		let cloned_properties = {
182			let mut map = HashMap::new();
183			for (key, value) in &self.properties {
184				let cloned_value = value.try_clone().unwrap_or_else(|err| {
185					panic!("Failure cloning 'props' field: {err:?}");
186				});
187				map.insert(key.clone(), cloned_value);
188			}
189			map
190		};
191
192		Self {
193			kind: self.kind.clone(),
194			detail1: self.detail1,
195			detail2: self.detail2,
196			any_data: cloned_any_data,
197			properties: cloned_properties,
198		}
199	}
200}
201
202/// Encapsulates the various different accessibility bus signal types.
203///
204/// Assumes being non exhaustive to allow for future- or custom signals.
205#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
206#[non_exhaustive]
207pub enum Event {
208	/// See: [`DocumentEvents`].
209	Document(DocumentEvents),
210	/// See: [`FocusEvents`].
211	Focus(FocusEvents),
212	/// See: [`KeyboardEvents`].
213	Keyboard(KeyboardEvents),
214	/// See: [`MouseEvents`].
215	Mouse(MouseEvents),
216	/// See: [`ObjectEvents`].
217	Object(ObjectEvents),
218	/// See: [`TerminalEvents`].
219	Terminal(TerminalEvents),
220	/// See: [`WindowEvents`].
221	Window(WindowEvents),
222	/// See: [`AvailableEvent`].
223	Available(AvailableEvent),
224	/// See: [`CacheEvents`].
225	Cache(CacheEvents),
226	/// See: [`EventListenerEvents`].
227	Listener(EventListenerEvents),
228}
229
230impl EventTypeProperties for Event {
231	fn member(&self) -> &'static str {
232		match self {
233			Self::Document(inner) => inner.member(),
234			Self::Focus(inner) => inner.member(),
235			Self::Keyboard(inner) => inner.member(),
236			Self::Mouse(inner) => inner.member(),
237			Self::Object(inner) => inner.member(),
238			Self::Terminal(inner) => inner.member(),
239			Self::Window(inner) => inner.member(),
240			Self::Available(inner) => inner.member(),
241			Self::Cache(inner) => inner.member(),
242			Self::Listener(inner) => inner.member(),
243		}
244	}
245	fn interface(&self) -> &'static str {
246		match self {
247			Self::Document(inner) => inner.interface(),
248			Self::Focus(inner) => inner.interface(),
249			Self::Keyboard(inner) => inner.interface(),
250			Self::Mouse(inner) => inner.interface(),
251			Self::Object(inner) => inner.interface(),
252			Self::Terminal(inner) => inner.interface(),
253			Self::Window(inner) => inner.interface(),
254			Self::Available(inner) => inner.interface(),
255			Self::Cache(inner) => inner.interface(),
256			Self::Listener(inner) => inner.interface(),
257		}
258	}
259	fn match_rule(&self) -> &'static str {
260		match self {
261			Self::Document(inner) => inner.match_rule(),
262			Self::Focus(inner) => inner.match_rule(),
263			Self::Keyboard(inner) => inner.match_rule(),
264			Self::Mouse(inner) => inner.match_rule(),
265			Self::Object(inner) => inner.match_rule(),
266			Self::Terminal(inner) => inner.match_rule(),
267			Self::Window(inner) => inner.match_rule(),
268			Self::Available(inner) => inner.match_rule(),
269			Self::Cache(inner) => inner.match_rule(),
270			Self::Listener(inner) => inner.match_rule(),
271		}
272	}
273	fn registry_string(&self) -> &'static str {
274		match self {
275			Self::Document(inner) => inner.registry_string(),
276			Self::Focus(inner) => inner.registry_string(),
277			Self::Keyboard(inner) => inner.registry_string(),
278			Self::Mouse(inner) => inner.registry_string(),
279			Self::Object(inner) => inner.registry_string(),
280			Self::Terminal(inner) => inner.registry_string(),
281			Self::Window(inner) => inner.registry_string(),
282			Self::Available(inner) => inner.registry_string(),
283			Self::Cache(inner) => inner.registry_string(),
284			Self::Listener(inner) => inner.registry_string(),
285		}
286	}
287}
288
289impl EventProperties for Event {
290	fn path(&self) -> ObjectPath<'_> {
291		match self {
292			Self::Document(inner) => inner.path(),
293			Self::Focus(inner) => inner.path(),
294			Self::Keyboard(inner) => inner.path(),
295			Self::Mouse(inner) => inner.path(),
296			Self::Object(inner) => inner.path(),
297			Self::Terminal(inner) => inner.path(),
298			Self::Window(inner) => inner.path(),
299			Self::Available(inner) => inner.path(),
300			Self::Cache(inner) => inner.path(),
301			Self::Listener(inner) => inner.path(),
302		}
303	}
304	fn sender(&self) -> UniqueName<'_> {
305		match self {
306			Self::Document(inner) => inner.sender(),
307			Self::Focus(inner) => inner.sender(),
308			Self::Keyboard(inner) => inner.sender(),
309			Self::Mouse(inner) => inner.sender(),
310			Self::Object(inner) => inner.sender(),
311			Self::Terminal(inner) => inner.sender(),
312			Self::Window(inner) => inner.sender(),
313			Self::Available(inner) => inner.sender(),
314			Self::Cache(inner) => inner.sender(),
315			Self::Listener(inner) => inner.sender(),
316		}
317	}
318}
319
320impl HasMatchRule for CacheEvents {
321	const MATCH_RULE_STRING: &'static str = "type='signal',interface='org.a11y.atspi.Event.Cache'";
322}
323
324impl HasRegistryEventString for CacheEvents {
325	const REGISTRY_EVENT_STRING: &'static str = "Cache";
326}
327
328impl HasMatchRule for EventListenerEvents {
329	const MATCH_RULE_STRING: &'static str =
330		"type='signal',interface='org.a11y.atspi.Event.Registry'";
331}
332
333impl HasRegistryEventString for EventListenerEvents {
334	const REGISTRY_EVENT_STRING: &'static str = "Event";
335}
336
337/// All events related to the `org.a11y.atspi.Cache` interface.
338/// Note that these are not telling the client that an item *has been added* to a cache.
339/// It is telling the client "here is a bunch of information to store it in your cache".
340#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Eq, Hash)]
341#[allow(clippy::module_name_repetitions)]
342pub enum CacheEvents {
343	/// See: [`AddAccessibleEvent`].
344	Add(AddAccessibleEvent),
345	/// See: [`LegacyAddAccessibleEvent`].
346	LegacyAdd(LegacyAddAccessibleEvent),
347	/// See: [`RemoveAccessibleEvent`].
348	Remove(RemoveAccessibleEvent),
349}
350
351impl EventTypeProperties for CacheEvents {
352	fn member(&self) -> &'static str {
353		match self {
354			Self::Add(inner) => inner.member(),
355			Self::LegacyAdd(inner) => inner.member(),
356			Self::Remove(inner) => inner.member(),
357		}
358	}
359	fn interface(&self) -> &'static str {
360		match self {
361			Self::Add(inner) => inner.interface(),
362			Self::LegacyAdd(inner) => inner.interface(),
363			Self::Remove(inner) => inner.interface(),
364		}
365	}
366	fn match_rule(&self) -> &'static str {
367		match self {
368			Self::Add(inner) => inner.match_rule(),
369			Self::LegacyAdd(inner) => inner.match_rule(),
370			Self::Remove(inner) => inner.match_rule(),
371		}
372	}
373	fn registry_string(&self) -> &'static str {
374		match self {
375			Self::Add(inner) => inner.registry_string(),
376			Self::LegacyAdd(inner) => inner.registry_string(),
377			Self::Remove(inner) => inner.registry_string(),
378		}
379	}
380}
381
382impl EventProperties for CacheEvents {
383	fn path(&self) -> ObjectPath<'_> {
384		match self {
385			Self::Add(inner) => inner.path(),
386			Self::LegacyAdd(inner) => inner.path(),
387			Self::Remove(inner) => inner.path(),
388		}
389	}
390	fn sender(&self) -> UniqueName<'_> {
391		match self {
392			Self::Add(inner) => inner.sender(),
393			Self::LegacyAdd(inner) => inner.sender(),
394			Self::Remove(inner) => inner.sender(),
395		}
396	}
397}
398
399/// Type that contains the `zbus::Message` for meta information and
400/// the [`crate::cache::LegacyCacheItem`]
401#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default, Eq, Hash)]
402pub struct LegacyAddAccessibleEvent {
403	/// The [`ObjectRef`] the event applies to.
404	pub item: ObjectRef,
405	/// A cache item to add to the internal cache.
406	pub node_added: LegacyCacheItem,
407}
408
409impl_from_user_facing_event_for_interface_event_enum!(
410	LegacyAddAccessibleEvent,
411	CacheEvents,
412	CacheEvents::LegacyAdd
413);
414impl_from_user_facing_type_for_event_enum!(LegacyAddAccessibleEvent, Event::Cache);
415impl_try_from_event_for_user_facing_type!(
416	LegacyAddAccessibleEvent,
417	CacheEvents::LegacyAdd,
418	Event::Cache
419);
420event_test_cases!(LegacyAddAccessibleEvent);
421impl_from_dbus_message!(LegacyAddAccessibleEvent);
422impl_event_properties!(LegacyAddAccessibleEvent);
423impl_to_dbus_message!(LegacyAddAccessibleEvent);
424
425impl BusProperties for LegacyAddAccessibleEvent {
426	const REGISTRY_EVENT_STRING: &'static str = "Cache:Add";
427	const MATCH_RULE_STRING: &'static str =
428		"type='signal',interface='org.a11y.atspi.Cache',member='AddAccessible'";
429	const DBUS_MEMBER: &'static str = "AddAccessible";
430	const DBUS_INTERFACE: &'static str = "org.a11y.atspi.Cache";
431
432	type Body = LegacyCacheItem;
433
434	fn from_message_parts(item: ObjectRef, body: Self::Body) -> Result<Self, AtspiError> {
435		Ok(Self { item, node_added: body })
436	}
437
438	fn body(&self) -> Self::Body {
439		self.node_added.clone()
440	}
441}
442
443/// Type that contains the `zbus::Message` for meta information and
444/// the [`crate::cache::CacheItem`]
445#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default, Eq, Hash)]
446pub struct AddAccessibleEvent {
447	/// The [`ObjectRef`] the event applies to.
448	pub item: ObjectRef,
449	/// A cache item to add to the internal cache.
450	pub node_added: CacheItem,
451}
452
453impl_from_user_facing_event_for_interface_event_enum!(
454	AddAccessibleEvent,
455	CacheEvents,
456	CacheEvents::Add
457);
458impl_from_user_facing_type_for_event_enum!(AddAccessibleEvent, Event::Cache);
459impl_try_from_event_for_user_facing_type!(AddAccessibleEvent, CacheEvents::Add, Event::Cache);
460event_test_cases!(AddAccessibleEvent);
461
462impl BusProperties for AddAccessibleEvent {
463	const REGISTRY_EVENT_STRING: &'static str = "Cache:Add";
464	const MATCH_RULE_STRING: &'static str =
465		"type='signal',interface='org.a11y.atspi.Cache',member='AddAccessible'";
466	const DBUS_MEMBER: &'static str = "AddAccessible";
467	const DBUS_INTERFACE: &'static str = "org.a11y.atspi.Cache";
468
469	type Body = CacheItem;
470
471	fn from_message_parts(item: ObjectRef, body: Self::Body) -> Result<Self, AtspiError> {
472		Ok(Self { item, node_added: body })
473	}
474
475	fn body(&self) -> Self::Body {
476		self.node_added.clone()
477	}
478}
479impl<T: BusProperties> HasMatchRule for T {
480	const MATCH_RULE_STRING: &'static str = <T as BusProperties>::MATCH_RULE_STRING;
481}
482impl<T: BusProperties> HasRegistryEventString for T {
483	const REGISTRY_EVENT_STRING: &'static str = <T as BusProperties>::REGISTRY_EVENT_STRING;
484}
485impl_from_dbus_message!(AddAccessibleEvent);
486impl_event_properties!(AddAccessibleEvent);
487impl_to_dbus_message!(AddAccessibleEvent);
488
489/// `Cache::RemoveAccessible` signal event type.
490#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default, Eq, Hash)]
491pub struct RemoveAccessibleEvent {
492	/// The application that emitted the signal TODO Check Me
493	/// The [`ObjectRef`] the event applies to.
494	pub item: ObjectRef,
495	/// The node that was removed from the application tree  TODO Check Me
496	pub node_removed: ObjectRef,
497}
498
499impl_from_user_facing_event_for_interface_event_enum!(
500	RemoveAccessibleEvent,
501	CacheEvents,
502	CacheEvents::Remove
503);
504impl_from_user_facing_type_for_event_enum!(RemoveAccessibleEvent, Event::Cache);
505impl_try_from_event_for_user_facing_type!(RemoveAccessibleEvent, CacheEvents::Remove, Event::Cache);
506event_test_cases!(RemoveAccessibleEvent);
507impl BusProperties for RemoveAccessibleEvent {
508	const REGISTRY_EVENT_STRING: &'static str = "Cache:Remove";
509	const MATCH_RULE_STRING: &'static str =
510		"type='signal',interface='org.a11y.atspi.Cache',member='RemoveAccessible'";
511	const DBUS_MEMBER: &'static str = "RemoveAccessible";
512	const DBUS_INTERFACE: &'static str = "org.a11y.atspi.Cache";
513
514	type Body = ObjectRef;
515
516	fn from_message_parts(item: ObjectRef, body: Self::Body) -> Result<Self, AtspiError> {
517		Ok(Self { item, node_removed: body })
518	}
519	fn body(&self) -> Self::Body {
520		self.node_removed.clone()
521	}
522}
523
524impl_from_dbus_message!(RemoveAccessibleEvent);
525impl_event_properties!(RemoveAccessibleEvent);
526impl_to_dbus_message!(RemoveAccessibleEvent);
527
528#[cfg(test)]
529mod accessible_deserialization_tests {
530	use crate::events::ObjectRef;
531	use zvariant::Value;
532
533	#[test]
534	fn try_into_value() {
535		let acc = ObjectRef::default();
536		let value_struct = Value::from(acc);
537		let Value::Structure(structure) = value_struct else {
538			panic!("Unable to destructure a structure out of the Value.");
539		};
540		let vals = structure.into_fields();
541		assert_eq!(vals.len(), 2);
542		let Value::Str(bus_name) = vals.first().unwrap() else {
543			panic!("Unable to destructure field value: {:?}", vals.first().unwrap());
544		};
545		assert_eq!(bus_name, ":0.0");
546		let Value::ObjectPath(path) = vals.last().unwrap() else {
547			panic!("Unable to destructure field value: {:?}", vals.get(1).unwrap());
548		};
549		assert_eq!(path.as_str(), "/org/a11y/atspi/accessible/null");
550	}
551	#[test]
552	fn try_from_value() {}
553}
554
555#[cfg(test)]
556mod accessible_tests {
557	use super::ObjectRef;
558
559	#[test]
560	fn test_accessible_default_doesnt_panic() {
561		let acc = ObjectRef::default();
562		assert_eq!(acc.name.as_str(), ":0.0");
563		assert_eq!(acc.path.as_str(), "/org/a11y/atspi/accessible/null");
564	}
565}
566#[cfg(feature = "zbus")]
567impl TryFrom<&zbus::Message> for ObjectRef {
568	type Error = AtspiError;
569	fn try_from(message: &zbus::Message) -> Result<Self, Self::Error> {
570		let header = message.header();
571		let path = header.path().expect("returned path is either `Some` or panics");
572		let owned_path: OwnedObjectPath = path.clone().into();
573
574		let sender: UniqueName<'_> = header.sender().expect("No sender in header").into();
575		let name: OwnedUniqueName = sender.to_owned().into();
576
577		Ok(ObjectRef { name, path: owned_path })
578	}
579}
580
581#[cfg(feature = "zbus")]
582impl TryFrom<&zbus::Message> for EventBodyOwned {
583	type Error = AtspiError;
584
585	fn try_from(message: &zbus::Message) -> Result<Self, Self::Error> {
586		let body = message.body();
587		let signature = body.signature().ok_or_else(|| AtspiError::MissingSignature)?;
588
589		if signature == QSPI_EVENT_SIGNATURE {
590			let qt_body = body.deserialize::<EventBodyQT>()?;
591			Ok(EventBodyOwned::from(qt_body))
592		} else if signature == ATSPI_EVENT_SIGNATURE {
593			Ok(body.deserialize::<EventBodyOwned>()?)
594		} else {
595			Err(AtspiError::Conversion(
596				"Unable to convert from zbus::Message to EventBodyQT or EventBodyOwned",
597			))
598		}
599	}
600}
601
602/// Signal type emitted by `EventListenerRegistered` and `EventListenerDeregistered` signals,
603/// which belong to the `Registry` interface, implemented by the registry-daemon.
604#[validate(signal: "EventListenerRegistered")]
605#[derive(Debug, Clone, Serialize, Deserialize, Type, PartialEq, Eq, Hash)]
606pub struct EventListeners {
607	pub bus_name: OwnedUniqueName,
608	pub path: String,
609}
610
611impl Default for EventListeners {
612	fn default() -> Self {
613		Self {
614			bus_name: UniqueName::try_from(":0.0").unwrap().into(),
615			path: "/org/a11y/atspi/accessible/null".to_string(),
616		}
617	}
618}
619
620#[cfg(test)]
621#[test]
622fn test_event_listener_default_no_panic() {
623	let el = EventListeners::default();
624	assert_eq!(el.bus_name.as_str(), ":0.0");
625	assert_eq!(el.path.as_str(), "/org/a11y/atspi/accessible/null");
626}
627
628/// Covers both `EventListener` events.
629#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
630#[allow(clippy::module_name_repetitions)]
631pub enum EventListenerEvents {
632	/// See: [`EventListenerRegisteredEvent`].
633	Registered(EventListenerRegisteredEvent),
634	/// See: [`EventListenerDeregisteredEvent`].
635	Deregistered(EventListenerDeregisteredEvent),
636}
637
638impl EventTypeProperties for EventListenerEvents {
639	fn member(&self) -> &'static str {
640		match self {
641			Self::Registered(inner) => inner.member(),
642			Self::Deregistered(inner) => inner.member(),
643		}
644	}
645	fn match_rule(&self) -> &'static str {
646		match self {
647			Self::Registered(inner) => inner.match_rule(),
648			Self::Deregistered(inner) => inner.match_rule(),
649		}
650	}
651	fn interface(&self) -> &'static str {
652		match self {
653			Self::Registered(inner) => inner.interface(),
654			Self::Deregistered(inner) => inner.interface(),
655		}
656	}
657	fn registry_string(&self) -> &'static str {
658		match self {
659			Self::Registered(inner) => inner.registry_string(),
660			Self::Deregistered(inner) => inner.registry_string(),
661		}
662	}
663}
664
665impl EventProperties for EventListenerEvents {
666	fn path(&self) -> ObjectPath<'_> {
667		match self {
668			Self::Registered(inner) => inner.path(),
669			Self::Deregistered(inner) => inner.path(),
670		}
671	}
672	fn sender(&self) -> UniqueName<'_> {
673		match self {
674			Self::Registered(inner) => inner.sender(),
675			Self::Deregistered(inner) => inner.sender(),
676		}
677	}
678}
679
680/// An event that is emitted by the registry daemon, to inform that an event has been deregistered
681/// to no longer listen for.
682#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default, Eq, Hash)]
683pub struct EventListenerDeregisteredEvent {
684	/// The [`ObjectRef`] the event applies to.
685	pub item: ObjectRef,
686	/// A list of events that have been deregistered via the registry interface.
687	/// See `atspi-connection`.
688	pub deregistered_event: EventListeners,
689}
690
691impl_from_user_facing_event_for_interface_event_enum!(
692	EventListenerDeregisteredEvent,
693	EventListenerEvents,
694	EventListenerEvents::Deregistered
695);
696impl_from_user_facing_type_for_event_enum!(EventListenerDeregisteredEvent, Event::Listener);
697impl_try_from_event_for_user_facing_type!(
698	EventListenerDeregisteredEvent,
699	EventListenerEvents::Deregistered,
700	Event::Listener
701);
702event_test_cases!(EventListenerDeregisteredEvent);
703impl BusProperties for EventListenerDeregisteredEvent {
704	const REGISTRY_EVENT_STRING: &'static str = "Registry:EventListenerDeregistered";
705	const MATCH_RULE_STRING: &'static str =
706		"type='signal',interface='org.a11y.atspi.Registry',member='EventListenerDeregistered'";
707	const DBUS_MEMBER: &'static str = "EventListenerDeregistered";
708	const DBUS_INTERFACE: &'static str = "org.a11y.atspi.Registry";
709
710	type Body = EventListeners;
711
712	fn from_message_parts(item: ObjectRef, body: Self::Body) -> Result<Self, AtspiError> {
713		Ok(Self { item, deregistered_event: body })
714	}
715	fn body(&self) -> Self::Body {
716		self.deregistered_event.clone()
717	}
718}
719impl_from_dbus_message!(EventListenerDeregisteredEvent);
720impl_event_properties!(EventListenerDeregisteredEvent);
721impl_to_dbus_message!(EventListenerDeregisteredEvent);
722
723/// An event that is emitted by the regostry daemon to signal that an event has been registered to listen for.
724#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default, Eq, Hash)]
725pub struct EventListenerRegisteredEvent {
726	/// The [`ObjectRef`] the event applies to.
727	pub item: ObjectRef,
728	/// A list of events that have been registered via the registry interface.
729	/// See `atspi-connection`.
730	pub registered_event: EventListeners,
731}
732
733impl_from_user_facing_event_for_interface_event_enum!(
734	EventListenerRegisteredEvent,
735	EventListenerEvents,
736	EventListenerEvents::Registered
737);
738impl_from_user_facing_type_for_event_enum!(EventListenerRegisteredEvent, Event::Listener);
739impl_try_from_event_for_user_facing_type!(
740	EventListenerRegisteredEvent,
741	EventListenerEvents::Registered,
742	Event::Listener
743);
744event_test_cases!(EventListenerRegisteredEvent);
745impl BusProperties for EventListenerRegisteredEvent {
746	const REGISTRY_EVENT_STRING: &'static str = "Registry:EventListenerRegistered";
747	const MATCH_RULE_STRING: &'static str =
748		"type='signal',interface='org.a11y.atspi.Registry',member='EventListenerRegistered'";
749	const DBUS_MEMBER: &'static str = "EventListenerRegistered";
750	const DBUS_INTERFACE: &'static str = "org.a11y.atspi.Registry";
751
752	type Body = EventListeners;
753
754	fn from_message_parts(item: ObjectRef, body: Self::Body) -> Result<Self, AtspiError> {
755		Ok(Self { item, registered_event: body })
756	}
757	fn body(&self) -> Self::Body {
758		self.registered_event.clone()
759	}
760}
761impl_from_dbus_message!(EventListenerRegisteredEvent);
762impl_event_properties!(EventListenerRegisteredEvent);
763impl_to_dbus_message!(EventListenerRegisteredEvent);
764
765/// An event that is emitted when the registry daemon has started.
766#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default, Eq, Hash)]
767pub struct AvailableEvent {
768	/// The [`ObjectRef`] the event applies to.
769	pub item: ObjectRef,
770	pub socket: ObjectRef,
771}
772impl From<AvailableEvent> for Event {
773	fn from(ev: AvailableEvent) -> Event {
774		Event::Available(ev)
775	}
776}
777impl TryFrom<Event> for AvailableEvent {
778	type Error = AtspiError;
779	fn try_from(generic_event: Event) -> Result<AvailableEvent, Self::Error> {
780		if let Event::Available(specific_event) = generic_event {
781			Ok(specific_event)
782		} else {
783			Err(AtspiError::Conversion("Invalid type"))
784		}
785	}
786}
787event_test_cases!(AvailableEvent);
788impl BusProperties for AvailableEvent {
789	const REGISTRY_EVENT_STRING: &'static str = "Socket:Available";
790	const MATCH_RULE_STRING: &'static str =
791		"type='signal',interface='org.a11y.atspi.Socket',member='Available'";
792	const DBUS_MEMBER: &'static str = "Available";
793	const DBUS_INTERFACE: &'static str = "org.a11y.atspi.Socket";
794
795	type Body = ObjectRef;
796
797	fn from_message_parts(item: ObjectRef, body: Self::Body) -> Result<Self, AtspiError> {
798		Ok(Self { item, socket: body })
799	}
800	fn body(&self) -> Self::Body {
801		self.socket.clone()
802	}
803}
804impl_from_dbus_message!(AvailableEvent);
805impl_event_properties!(AvailableEvent);
806impl_to_dbus_message!(AvailableEvent);
807
808#[cfg(feature = "zbus")]
809impl TryFrom<&zbus::Message> for Event {
810	type Error = AtspiError;
811
812	fn try_from(msg: &zbus::Message) -> Result<Event, AtspiError> {
813		let body = msg.body();
814		let header = msg.header();
815
816		let body_signature = body.signature().ok_or(AtspiError::MissingSignature)?;
817		let body_signature_str = body_signature.as_str();
818
819		let member = header.member().ok_or(AtspiError::MissingMember)?;
820		let member_str = member.as_str();
821
822		let interface = header.interface().ok_or(AtspiError::MissingInterface)?;
823		let interface_str = interface.as_str();
824
825		// The `body_signature` is a marshalled D-Bus signatures, this means that outer STRUCT
826		// parentheses are not included in the signature.
827		// However, `Cache` signals are often emitted with outer parentheses, so we also need to
828		// match against the same signature, but with outer parentheses.
829		match (interface_str, member_str, body_signature_str) {
830			("org.a11y.atspi.Socket", "Available", "so") => {
831				Ok(AvailableEvent::try_from(msg)?.into())
832			}
833			("org.a11y.atspi.Event.Object", _, "siiva{sv}" | "siiv(so)") => {
834				Ok(Event::Object(ObjectEvents::try_from(msg)?))
835			}
836			("org.a11y.atspi.Event.Document", _, "siiva{sv}" | "siiv(so)") => {
837				Ok(Event::Document(DocumentEvents::try_from(msg)?))
838			}
839			("org.a11y.atspi.Event.Window", _, "siiva{sv}" | "siiv(so)") => {
840				Ok(Event::Window(WindowEvents::try_from(msg)?))
841			}
842			("org.a11y.atspi.Event.Terminal", _, "siiva{sv}" | "siiv(so)") => {
843				Ok(Event::Terminal(TerminalEvents::try_from(msg)?))
844			}
845			("org.a11y.atspi.Event.Mouse", _, "siiva{sv}" | "siiv(so)") => {
846				Ok(Event::Mouse(MouseEvents::try_from(msg)?))
847			}
848			("org.a11y.atspi.Event.Focus", _, "siiva{sv}" | "siiv(so)") => {
849				Ok(Event::Focus(FocusEvents::try_from(msg)?))
850			}
851			("org.a11y.atspi.Event.Keyboard", _, "siiva{sv}" | "siiv(so)") => {
852				Ok(Event::Keyboard(KeyboardEvents::try_from(msg)?))
853			}
854			("org.a11y.atspi.Registry", "EventListenerRegistered", "ss") => {
855				Ok(EventListenerRegisteredEvent::try_from(msg)?.into())
856			}
857			("org.a11y.atspi.Registry", "EventListenerDeregistered", "ss") => {
858				Ok(EventListenerDeregisteredEvent::try_from(msg)?.into())
859			}
860			(
861				"org.a11y.atspi.Cache",
862				"AddAccessible",
863				"(so)(so)(so)iiassusau" | "((so)(so)(so)iiassusau)",
864			) => Ok(AddAccessibleEvent::try_from(msg)?.into()),
865			(
866				"org.a11y.atspi.Cache",
867				"AddAccessible",
868				"(so)(so)(so)a(so)assusau" | "((so)(so)(so)a(so)assusau)",
869			) => Ok(LegacyAddAccessibleEvent::try_from(msg)?.into()),
870			("org.a11y.atspi.Cache", "RemoveAccessible", "so" | "(so)") => {
871				Ok(RemoveAccessibleEvent::try_from(msg)?.into())
872			}
873			(_iface, _method, sig) => Err(AtspiError::UnknownBusSignature(sig.to_string())),
874		}
875	}
876}
877
878/// Describes properties of a specific event _type_.
879///
880/// - `DBus` member name
881/// - `DBus` interface name
882///
883/// Together, the member and interface name can describe a specific event _type_.
884/// Likewise, the path and sender bus name collectively make up an [`ObjectRef`], which is a way to uniquely identify an individual accessible item available to `atspi`.
885/// The latter is available via the [`EventProperties`] trait.
886///
887/// This can also be generalized, for example this is implemented for [`Event`] by dispatching to the matching variants.
888/// NOTE: to use `EventProperties` on wrapper types, like `Event`, you must enable the "enum-dispatch" feature.
889///
890/// This trait *is* object-safe.
891pub trait EventTypeProperties {
892	fn member(&self) -> &'static str;
893	fn interface(&self) -> &'static str;
894	fn match_rule(&self) -> &'static str;
895	fn registry_string(&self) -> &'static str;
896}
897
898impl<T: BusProperties> EventTypeProperties for T {
899	fn member(&self) -> &'static str {
900		<T>::DBUS_MEMBER
901	}
902	fn interface(&self) -> &'static str {
903		<T>::DBUS_INTERFACE
904	}
905	fn match_rule(&self) -> &'static str {
906		<T>::MATCH_RULE_STRING
907	}
908	fn registry_string(&self) -> &'static str {
909		<T>::REGISTRY_EVENT_STRING
910	}
911}
912
913assert_obj_safe!(EventTypeProperties);
914
915/// `EventProperties` allows access to the internals of an event, specifically:
916///
917/// - The `DBUs` name which sent the event.
918/// - The `ObjectPath`, a unique id for a given application.
919/// - Collectively, this is called an [`ObjectRef`].
920///
921/// This trait *is* object-safe.
922pub trait EventProperties {
923	fn sender(&self) -> UniqueName<'_>;
924	fn path(&self) -> ObjectPath<'_>;
925	fn object_ref(&self) -> ObjectRef {
926		ObjectRef { name: self.sender().into(), path: self.path().into() }
927	}
928}
929
930assert_obj_safe!(EventProperties);
931
932/// Describes the `DBus`-related information about a given struct.
933///
934/// - `DBus` member name
935/// - `DBus` interface name
936/// - `DBus` match string: used to tell `DBus` you are interested in a particular signal
937/// - accessibility registry event string: used to tell the accessibility registry that you are interested in a particular event
938///
939/// This trait *is not* object-safe.
940/// For a similar, but object-safe trait, see [`EventProperties`].
941pub trait BusProperties {
942	/// The `DBus` member for the event.
943	/// For example, for an [`object::TextChangedEvent`] this should be `"TextChanged"`
944	const DBUS_MEMBER: &'static str;
945	/// The `DBus` interface name for this event.
946	/// For example, for any event within [`object`], this should be "org.a11y.atspi.Event.Object".
947	const DBUS_INTERFACE: &'static str;
948	/// A static match rule string for `DBus`.
949	/// This should usually be a string that looks like this: `"type='signal',interface='org.a11y.atspi.Event.Object',member='PropertyChange'"`;
950	/// This should be deprecated in favour of composing the string from [`Self::DBUS_MEMBER`] and [`Self::DBUS_INTERFACE`].
951	const MATCH_RULE_STRING: &'static str;
952	/// A registry event string for registering for event receiving via the `RegistryProxy`.
953	/// This should be deprecated in favour of composing the string from [`Self::DBUS_MEMBER`] and [`Self::DBUS_INTERFACE`].
954	const REGISTRY_EVENT_STRING: &'static str;
955
956	/// What is the body type of this event.
957	type Body: Type + Serialize + for<'a> Deserialize<'a>;
958
959	/// Build the event from the object pair (`ObjectRef` and the Body).
960	///
961	/// # Errors
962	///
963	/// 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.
964	/// 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.
965	fn from_message_parts(item: ObjectRef, body: Self::Body) -> Result<Self, AtspiError>
966	where
967		Self: Sized;
968
969	/// The body of the object.
970	fn body(&self) -> Self::Body;
971}
972
973/// A specific trait *only* to define match rules.
974/// This is useful for event wrappers like [`ObjectEvents`], which, while it does not have other
975/// information required to implement the [`BusProperties`] trait, you can indeed add a match rule
976/// to the `DBus` connection to capture all sub events of [`ObjectEvents`].
977///
978/// This trait *is not* object-safe.
979pub trait HasMatchRule {
980	/// A static match rule string for `DBus`.
981	/// This should usually be a string that looks like this: `"type='signal',interface='org.a11y.atspi.Event.Object',member='PropertyChange'"`;
982	/// This should be deprecated in favour of composing the string from [`BusProperties::DBUS_MEMBER`] and [`BusProperties::DBUS_INTERFACE`].
983	const MATCH_RULE_STRING: &'static str;
984}
985
986/// A specific trait *only* to define registry event matches.
987/// This is useful for event wrappers like [`ObjectEvents`], which, while it does not have other
988/// information required to implement the [`BusProperties`] trait, you can indeed add a match rule
989/// to the AT-SPI connection to subscribe to all sub events of [`ObjectEvents`].
990///
991/// This trait *is not* object-safe.
992pub trait HasRegistryEventString {
993	/// A registry event string for registering for event receiving via the `RegistryProxy`.
994	/// This should be deprecated in favour of composing the string from [`BusProperties::DBUS_MEMBER`] and [`BusProperties::DBUS_INTERFACE`].
995	const REGISTRY_EVENT_STRING: &'static str;
996}
997
998#[cfg(test)]
999mod tests {
1000	use super::{EventBodyOwned, EventBodyQT, QSPI_EVENT_SIGNATURE};
1001	use std::collections::HashMap;
1002	use zvariant::{ObjectPath, Type};
1003
1004	#[test]
1005	fn check_event_body_qt_signature() {
1006		assert_eq!(&<EventBodyQT as Type>::signature(), &QSPI_EVENT_SIGNATURE);
1007	}
1008
1009	#[test]
1010	fn test_event_body_qt_to_event_body_owned_conversion() {
1011		let event_body: EventBodyOwned = EventBodyQT::default().into();
1012
1013		let accessible = crate::ObjectRef::default();
1014		let name = accessible.name;
1015		let path = accessible.path;
1016		let props = HashMap::from([(name, ObjectPath::from(path).into())]);
1017		assert_eq!(event_body.properties, props);
1018	}
1019}