use std::sync::{Arc, OnceLock};
pub use xa11y_core::{
App, Element, ElementData, ElementState, Error, Event, EventKind, Locator, RawPlatformData,
Rect, Result, Role, StateFlag, StateSet, Subscription, SubscriptionIter, Toggled,
};
pub use xa11y_core::input;
pub use xa11y_core::{
anchor_point, point_for, Anchor, ClickOptions, ClickTarget, DragOptions, InputProvider,
InputSim, IntoPoint, Key, Keyboard, Mouse, MouseButton, Point, ScrollDelta,
};
pub use xa11y_core::screenshot;
pub use xa11y_core::{Screenshot, ScreenshotProvider};
#[doc(hidden)]
pub use xa11y_core::{CancelHandle, EventReceiver, Provider, RecvStatus, Selector};
#[cfg(feature = "test-support")]
#[doc(hidden)]
pub use xa11y_core::mock;
#[doc(hidden)]
pub mod cli;
pub use app_ext::AppExt;
static PROVIDER: OnceLock<std::result::Result<&'static dyn Provider, String>> = OnceLock::new();
fn get_provider_ref() -> Result<&'static dyn Provider> {
PROVIDER
.get_or_init(|| {
create_provider_boxed()
.map(|b| &*Box::leak(b))
.map_err(|e| format!("{e}"))
})
.as_ref()
.copied()
.map_err(|msg| Error::Platform {
code: -1,
message: msg.clone(),
})
}
#[doc(hidden)]
pub fn provider() -> Result<Arc<dyn Provider>> {
Ok(Arc::new(get_provider_ref()?))
}
#[doc(hidden)]
#[cfg(feature = "testing")]
pub fn create_provider() -> Result<Arc<dyn Provider>> {
create_provider_boxed().map(Arc::from)
}
pub fn input_sim() -> Result<InputSim> {
#[cfg(target_os = "macos")]
{
let backend = xa11y_macos::MacOSInputProvider::new()?;
Ok(InputSim::new(Arc::new(backend)))
}
#[cfg(target_os = "windows")]
{
let backend = xa11y_windows::WindowsInputProvider::new()?;
Ok(InputSim::new(Arc::new(backend)))
}
#[cfg(target_os = "linux")]
{
let backend = xa11y_linux::LinuxInputProvider::new()?;
Ok(InputSim::new(Arc::new(backend)))
}
#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "linux")))]
{
Err(Error::Platform {
code: -1,
message: format!(
"Input simulation not available on platform: {}",
std::env::consts::OS
),
})
}
}
static SCREENSHOT_BACKEND: OnceLock<std::result::Result<Arc<dyn ScreenshotProvider>, String>> =
OnceLock::new();
fn screenshot_backend() -> Result<Arc<dyn ScreenshotProvider>> {
SCREENSHOT_BACKEND
.get_or_init(create_screenshot_backend)
.as_ref()
.cloned()
.map_err(|msg| Error::Platform {
code: -1,
message: msg.clone(),
})
}
fn create_screenshot_backend() -> std::result::Result<Arc<dyn ScreenshotProvider>, String> {
#[cfg(target_os = "macos")]
{
xa11y_macos::MacOSScreenshot::new()
.map(|b| Arc::new(b) as Arc<dyn ScreenshotProvider>)
.map_err(|e| format!("{e}"))
}
#[cfg(target_os = "windows")]
{
xa11y_windows::WindowsScreenshot::new()
.map(|b| Arc::new(b) as Arc<dyn ScreenshotProvider>)
.map_err(|e| format!("{e}"))
}
#[cfg(target_os = "linux")]
{
xa11y_linux::LinuxScreenshot::new()
.map(|b| Arc::new(b) as Arc<dyn ScreenshotProvider>)
.map_err(|e| format!("{e}"))
}
#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "linux")))]
{
Err(format!(
"Screenshot not available on platform: {}",
std::env::consts::OS
))
}
}
pub fn screenshot() -> Result<Screenshot> {
screenshot_backend()?.capture_full()
}
pub fn screenshot_region(rect: Rect) -> Result<Screenshot> {
screenshot_backend()?.capture_region(rect)
}
pub fn screenshot_element(element: &Element) -> Result<Screenshot> {
let rect = element.bounds.ok_or(Error::NoElementBounds)?;
screenshot_backend()?.capture_region(rect)
}
fn create_provider_boxed() -> Result<Box<dyn Provider>> {
#[cfg(target_os = "macos")]
{
Ok(Box::new(xa11y_macos::MacOSProvider::new()?))
}
#[cfg(target_os = "windows")]
{
Ok(Box::new(xa11y_windows::WindowsProvider::new()?))
}
#[cfg(target_os = "linux")]
{
Ok(Box::new(xa11y_linux::LinuxProvider::new()?))
}
#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "linux")))]
{
Err(Error::Platform {
code: -1,
message: format!("Unsupported platform: {}", std::env::consts::OS),
})
}
}
mod app_ext {
use std::time::Duration;
use super::{provider, App, Result};
pub trait AppExt: Sized {
fn by_name(name: &str) -> Result<Self>;
fn by_name_timeout(name: &str, timeout: Duration) -> Result<Self>;
fn by_pid(pid: u32) -> Result<Self>;
fn by_pid_timeout(pid: u32, timeout: Duration) -> Result<Self>;
fn list() -> Result<Vec<Self>>;
}
impl AppExt for App {
fn by_name(name: &str) -> Result<Self> {
App::by_name_with(provider()?, name)
}
fn by_name_timeout(name: &str, timeout: Duration) -> Result<Self> {
App::by_name_with_timeout(provider()?, name, timeout)
}
fn by_pid(pid: u32) -> Result<Self> {
App::by_pid_with(provider()?, pid)
}
fn by_pid_timeout(pid: u32, timeout: Duration) -> Result<Self> {
App::by_pid_with_timeout(provider()?, pid, timeout)
}
fn list() -> Result<Vec<Self>> {
App::list_with(provider()?)
}
}
}