1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
use std::sync::Arc;
use crate::element::{Element, ElementData};
use crate::error::{Error, Result};
use crate::event_provider::Subscription;
use crate::locator::Locator;
use crate::provider::Provider;
use crate::selector::Selector;
/// A running application, the entry point for accessibility queries.
///
/// `App` is **not** an [`Element`] — it represents the application as a whole
/// and provides a [`locator`](App::locator) to search its accessibility tree.
pub struct App {
/// Application name.
pub name: String,
/// Process ID.
pub pid: Option<u32>,
/// The underlying element data for this application.
pub data: ElementData,
provider: Arc<dyn Provider>,
}
impl App {
/// Find an application by exact name, using an explicit provider.
///
/// Prefer `App::by_name` from the `xa11y` crate which uses the global
/// singleton provider. Use this variant when you need to supply a specific
/// provider (e.g. a mock in unit tests).
///
/// Returns [`Error::PermissionDenied`] if the platform provider was created
/// without required permissions.
pub fn by_name_with(provider: Arc<dyn Provider>, name: &str) -> Result<Self> {
// Try application role first (Linux/macOS), then window role (Windows —
// UIA has no Application node at the top level).
let app_selector = format!(r#"application[name="{}"]"#, name);
if let Ok(results) =
provider.find_elements(None, &Selector::parse(&app_selector)?, Some(1), Some(0))
{
if let Some(data) = results.into_iter().next() {
return Ok(Self::from_data(provider, data));
}
}
let win_selector = format!(r#"window[name="{}"]"#, name);
let results =
provider.find_elements(None, &Selector::parse(&win_selector)?, Some(1), Some(0))?;
let data = results
.into_iter()
.next()
.ok_or(Error::SelectorNotMatched {
selector: app_selector,
})?;
Ok(Self::from_data(provider, data))
}
/// Find an application by process ID, using an explicit provider.
///
/// Prefer `App::by_pid` from the `xa11y` crate which uses the global
/// singleton provider.
pub fn by_pid_with(provider: Arc<dyn Provider>, pid: u32) -> Result<Self> {
// Try application role first, then window role (Windows fallback).
for role in ["application", "window"] {
let selector = Selector::parse(role)?;
if let Ok(results) = provider.find_elements(None, &selector, None, Some(0)) {
if let Some(data) = results.into_iter().find(|d| d.pid == Some(pid)) {
return Ok(Self::from_data(provider, data));
}
}
}
Err(Error::SelectorNotMatched {
selector: format!("application with pid={}", pid),
})
}
/// List all running applications, using an explicit provider.
///
/// Prefer `App::list` from the `xa11y` crate which uses the global
/// singleton provider.
pub fn list_with(provider: Arc<dyn Provider>) -> Result<Vec<Self>> {
// Collect application role elements (Linux/macOS), then add window
// role elements (Windows fallback) for any not already found by PID.
let mut apps = Vec::new();
let mut seen_pids = std::collections::HashSet::new();
for role in ["application", "window"] {
let selector = Selector::parse(role)?;
if let Ok(results) = provider.find_elements(None, &selector, None, Some(0)) {
for d in results {
if let Some(pid) = d.pid {
if !seen_pids.insert(pid) {
continue; // already found via application role
}
}
apps.push(Self::from_data(Arc::clone(&provider), d));
}
}
}
Ok(apps)
}
fn from_data(provider: Arc<dyn Provider>, data: ElementData) -> Self {
let name = data.name.clone().unwrap_or_default();
let pid = data.pid;
Self {
name,
pid,
data,
provider,
}
}
/// Create a [`Locator`] to search this application's accessibility tree.
pub fn locator(&self, selector: &str) -> Locator {
Locator::new(
Arc::clone(&self.provider),
Some(self.data.clone()),
selector,
)
}
/// Subscribe to accessibility events from this application.
pub fn subscribe(&self) -> Result<Subscription> {
self.provider.subscribe(&self.data)
}
/// Get direct children (typically windows) of this application.
pub fn children(&self) -> Result<Vec<Element>> {
let children = self.provider.get_children(Some(&self.data))?;
Ok(children
.into_iter()
.map(|d| Element::new(d, Arc::clone(&self.provider)))
.collect())
}
/// Get the provider reference.
pub fn provider(&self) -> &Arc<dyn Provider> {
&self.provider
}
}
impl std::fmt::Display for App {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "application \"{}\"", self.name)
}
}
impl std::fmt::Debug for App {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("App")
.field("name", &self.name)
.field("pid", &self.pid)
.finish()
}
}