atspi_common/
lib.rs

1#![deny(clippy::all, clippy::pedantic, clippy::cargo, unsafe_code, rustdoc::all)]
2#![allow(clippy::module_name_repetitions)]
3#![allow(clippy::multiple_crate_versions)]
4
5//! # atspi-common
6//!
7//! Defines all common types, events, and data structures for `atspi-proxies` and `atspi-connection`.
8//! Since `atspi-proxies` and `atspi-connection` are downstream crates, the documentation can not link to it directly.
9//! Any type ending in `*Proxy` is in `atspi-proxies`.
10//!
11
12#[macro_use]
13extern crate static_assertions;
14#[macro_use]
15pub(crate) mod macros;
16
17pub mod action;
18#[cfg(feature = "wrappers")]
19pub use crate::events::event_wrappers::{
20	CacheEvents, DocumentEvents, Event, EventListenerEvents, FocusEvents, KeyboardEvents,
21	MouseEvents, ObjectEvents, TerminalEvents, WindowEvents,
22};
23pub use action::Action;
24pub mod object_match;
25pub use object_match::{MatchType, ObjectMatchRule, SortOrder, TreeTraversalType};
26pub mod object_ref;
27pub use object_ref::ObjectRef;
28pub mod operation;
29pub use operation::Operation;
30pub mod interface;
31pub use interface::{Interface, InterfaceSet};
32pub mod state;
33pub use state::{State, StateSet};
34pub mod cache;
35pub use cache::{CacheItem, LegacyCacheItem};
36pub mod error;
37pub use error::AtspiError;
38pub mod events;
39pub use events::{EventProperties, EventTypeProperties};
40mod role;
41pub use role::Role;
42mod relation_type;
43pub use relation_type::RelationType;
44
45use serde::{Deserialize, Serialize};
46use zvariant::Type;
47
48pub type Result<T> = std::result::Result<T, AtspiError>;
49
50/// Describes a selection of text, including selections across object boundaries.
51///
52/// For example, selecting from the beginning of a paragraph to half way through a link would cause
53/// the start and end object references to be different.
54#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, Type)]
55pub struct TextSelection {
56	/// starting object reference
57	start_obj: ObjectRef,
58	/// text offset within `start_obj`
59	start_idx: i32,
60	/// ending object reference
61	end_obj: ObjectRef,
62	/// text offset within `end_obj`
63	end_idx: i32,
64	/// is the `start_obj` active;
65	///
66	/// This is the same as querying for the [`StateSet`], then checking if [`State::Active`] is contained.
67	/// See `atspi_proxies::accessible::AccessibleProxy` for more information on checking state.
68	start_is_active: bool,
69}
70
71#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize, Type)]
72#[repr(u32)]
73/// The coordinate type encodes the frame of reference.
74pub enum CoordType {
75	/// In relation to the entire screen.
76	Screen,
77	/// In relation to only the window.
78	Window,
79	/// In relation to the parent of the element being checked.
80	Parent,
81}
82
83#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, Serialize, Deserialize, Type)]
84#[repr(u32)]
85/// Enumeration used by `TextProxy` to indicate how to treat characters intersecting bounding boxes.
86pub enum ClipType {
87	/// No characters/glyphs are omitted.
88	Neither,
89	/// Characters/glyphs clipped by the minimum coordinate are omitted.
90	Min,
91	/// Characters/glyphs which intersect the maximum coordinate are omitted.
92	Max,
93	/// Only glyphs falling entirely within the region bounded by min and max are retained.
94	Both,
95}
96
97#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, Serialize, Deserialize, Type)]
98#[repr(u32)]
99/// Level of granularity to get text of, in relation to a cursor position.
100pub enum Granularity {
101	/// Gives the character at the index of the cursor. With a line-style cursor (which is standard) this will get the character that appears after the cursor.
102	Char,
103	/// Gives the entire word in front of, or which contains, the cursor. TODO: confirm that it always chooses the word in front of the cursor.
104	Word,
105	/// Gives entire sentence in front of, or which contains, the cursor. TODO: confirm that it always chooses the sentence after the cursor.
106	Sentence,
107	/// Gives the line, as seen visually of which the cursor is situated within.
108	Line,
109	/// Gives the entire block of text, regardless of where the cursor lies within it.
110	Paragraph,
111}
112
113/// Indicates relative stacking order of a `atspi_proxies::component::ComponentProxy` with respect to the
114/// onscreen visual representation of the UI.
115///
116/// The layer index, in combination with the component's extents,
117/// can be used to compute the visibility of all or part of a component.
118/// This is important in programmatic determination of region-of-interest for magnification,
119/// and in flat screen review models of the screen, as well as for other uses.
120/// Objects residing in two of the `Layer` categories support further z-ordering information,
121/// with respect to their peers in the same layer:
122/// namely, [`Layer::Window`] and [`Layer::Mdi`].
123/// Relative stacking order for other objects within the same layer is not available;
124/// the recommended heuristic is first child paints first. In other words,
125/// assume that the first siblings in the child list are subject to being
126/// overpainted by later siblings if their bounds intersect.
127#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize, Type)]
128pub enum Layer {
129	/// Indicates an error condition or uninitialized value.
130	Invalid,
131	/// Reserved for the desktop background; this is the bottom-most layer,
132	/// over which everything else is painted.
133	Background,
134	/// The 'background' layer for most content renderers and
135	/// UI `atspi_proxies::component::ComponentProxy` containers.
136	Canvas,
137	/// The layer in which the majority of ordinary 'foreground' widgets reside.
138	Widget,
139	/// A special layer between [`Layer::Canvas`] and [`Layer::Widget`], in which the
140	/// 'pseudo windows' (e.g. the Multiple-Document Interface frames) reside.
141	///
142	/// See `atspi_proxies::component::ComponentProxy::get_mdizorder`.
143	Mdi,
144	/// A layer for popup window content, above [`Layer::Widget`].
145	Popup,
146	/// The topmost layer.
147	Overlay,
148	/// The layer in which a toplevel window background usually resides.
149	Window,
150}
151
152/// Enumeration used by interface the [`crate::interface::Interface::Accessible`] to specify where an object should be placed on the screen when using `scroll_to`.
153#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize, Type)]
154pub enum ScrollType {
155	/// Scroll the object to the top left corner of the window.
156	TopLeft,
157	/// Scroll the object to the bottom right corner of the window.
158	BottomRight,
159	/// Scroll the object to the top edge of the window.
160	TopEdge,
161	/// Scroll the object to the bottom edge of the window.
162	BottomEdge,
163	/// Scroll the object to the left edge of the window.
164	LeftEdge,
165	/// Scroll the object to the right edge of the window.
166	RightEdge,
167	/// Scroll the object to application-dependent position on the window.
168	Anywhere,
169}
170
171/// Enumeration used to indicate a type of live region and how assertive it
172/// should be in terms of speaking notifications.
173///
174/// Currently, this is only used
175/// for `Announcement` events, but it may be used for additional purposes
176/// in the future.
177/// The argument in the `Announcement` event is named `politeness`.
178#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash, Serialize, Deserialize, Type)]
179#[repr(i32)]
180pub enum Politeness {
181	/// No live region.
182	#[default]
183	None = 0,
184	/// This live region should be considered polite.
185	Polite = 1,
186	/// This live region should be considered assertive.
187	Assertive = 2,
188}
189
190impl TryFrom<i32> for Politeness {
191	type Error = AtspiError;
192
193	fn try_from(value: i32) -> std::result::Result<Self, Self::Error> {
194		match value {
195			0 => Ok(Politeness::None),
196			1 => Ok(Politeness::Polite),
197			2 => Ok(Politeness::Assertive),
198			_ => Err(AtspiError::Conversion("Unknown Politeness variant")),
199		}
200	}
201}
202
203#[cfg(test)]
204mod tests {
205	use super::*;
206	use std::str::FromStr;
207	use zbus_lockstep::{
208		method_args_signature, method_return_signature, signal_body_type_signature,
209	};
210	use zvariant::Signature;
211
212	#[test]
213	fn convert_i32_to_live() {
214		assert_eq!(Politeness::None, Politeness::try_from(0).unwrap());
215		assert_eq!(Politeness::Polite, Politeness::try_from(1).unwrap());
216		assert_eq!(Politeness::Assertive, Politeness::try_from(2).unwrap());
217		assert!(Politeness::try_from(3).is_err());
218		assert!(Politeness::try_from(-1).is_err());
219	}
220
221	#[test]
222	fn validate_live_signature() {
223		let signature = signal_body_type_signature!("Announcement");
224		let politeness_signature_str = &signature.to_string_no_parens();
225		let politeness_signature = Signature::from_str(&politeness_signature_str.as_str()[1..2])
226			.expect("Valid signature pattern");
227		assert_eq!(*<Politeness as Type>::SIGNATURE, politeness_signature);
228	}
229
230	#[test]
231	fn validate_scroll_type_signature() {
232		let signature = method_args_signature!(member: "ScrollTo", interface: "org.a11y.atspi.Component", argument: "type");
233		assert_eq!(*<ScrollType as Type>::SIGNATURE, signature);
234	}
235
236	#[test]
237	fn validate_layer_signature() {
238		let signature = method_return_signature!("GetLayer");
239		assert_eq!(*<Layer as Type>::SIGNATURE, signature);
240	}
241
242	#[test]
243	fn validate_granularity_signature() {
244		let signature = method_args_signature!(member: "GetStringAtOffset", interface: "org.a11y.atspi.Text", argument: "granularity");
245		assert_eq!(*<Granularity as Type>::SIGNATURE, signature);
246	}
247
248	#[test]
249	fn validate_clip_type_signature() {
250		let signature = method_args_signature!(member: "GetTextAtOffset", interface: "org.a11y.atspi.Text", argument: "type");
251		assert_eq!(*<ClipType as Type>::SIGNATURE, signature);
252	}
253
254	#[test]
255	fn validate_coord_type_signature() {
256		let signature = method_args_signature!(member: "GetImagePosition", interface: "org.a11y.atspi.Image", argument: "coordType");
257		assert_eq!(*<CoordType as Type>::SIGNATURE, signature);
258	}
259
260	#[test]
261	fn validate_match_type_signature() {
262		let rule_signature = method_args_signature!(member: "GetMatchesTo", interface: "org.a11y.atspi.Collection", argument: "rule");
263		let match_type_signature_str = rule_signature.to_string();
264		let match_type_signature = Signature::from_str(&match_type_signature_str.as_str()[3..4])
265			.expect("Valid signature pattern");
266		assert_eq!(*<MatchType as Type>::SIGNATURE, match_type_signature);
267	}
268
269	#[test]
270	fn validate_text_selection_signature() {
271		let selection_signature = method_args_signature!(member: "GetTextSelections", interface: "org.a11y.atspi.Document", argument: "selections");
272		let selection_signature_str = selection_signature.to_string();
273		let selection_signature = Signature::from_str(&selection_signature_str.as_str()[1..])
274			.expect("Valid signature pattern");
275		// this signature is written: `a(...)`, where `(...)` is the signature we want to compare against
276		assert_eq!(*<TextSelection as Type>::SIGNATURE, selection_signature);
277	}
278}