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