use std::ops::Range;
use crate::app::AppInfo;
use crate::element::RawNode;
use crate::error::{Error, Result};
use crate::geometry::Point;
use crate::keys::KeyChord;
use crate::screenshot::RawImage;
use crate::state::WindowInfo;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
#[non_exhaustive]
pub enum MouseButton {
#[default]
Left,
Right,
Middle,
}
impl std::str::FromStr for MouseButton {
type Err = Error;
fn from_str(s: &str) -> Result<Self> {
match s.trim().to_ascii_lowercase().as_str() {
"left" | "primary" => Ok(Self::Left),
"right" | "secondary" => Ok(Self::Right),
"middle" | "center" => Ok(Self::Middle),
other => Err(Error::invalid(format!("unknown mouse button {other:?}"))),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum ScrollDirection {
Up,
Down,
Left,
Right,
}
impl ScrollDirection {
#[must_use]
pub const fn as_str(self) -> &'static str {
match self {
Self::Up => "up",
Self::Down => "down",
Self::Left => "left",
Self::Right => "right",
}
}
}
impl std::str::FromStr for ScrollDirection {
type Err = Error;
fn from_str(s: &str) -> Result<Self> {
match s.trim().to_ascii_lowercase().as_str() {
"up" => Ok(Self::Up),
"down" => Ok(Self::Down),
"left" => Ok(Self::Left),
"right" => Ok(Self::Right),
other => Err(Error::invalid(format!("unknown scroll direction {other:?}"))),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
#[non_exhaustive]
pub enum Delivery {
#[default]
Background,
FocusedBackground,
Global,
}
#[derive(Debug, Clone, Copy)]
pub struct InputTarget<'a> {
pub app: &'a AppInfo,
pub window: Option<&'a WindowInfo>,
pub delivery: Delivery,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub struct Permissions {
pub accessibility: bool,
pub screen_recording: bool,
}
impl Permissions {
pub const GRANTED: Self = Self { accessibility: true, screen_recording: true };
#[must_use]
pub const fn new(accessibility: bool, screen_recording: bool) -> Self {
Self { accessibility, screen_recording }
}
#[must_use]
pub const fn all_granted(&self) -> bool {
self.accessibility && self.screen_recording
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub struct CaptureRequest {
pub include_tree: bool,
pub include_screenshot: bool,
pub max_nodes: usize,
pub max_depth: usize,
pub window: Option<u64>,
pub include_menu_bar: bool,
}
impl CaptureRequest {
#[must_use]
pub const fn new(include_tree: bool, include_screenshot: bool, max_nodes: usize, max_depth: usize) -> Self {
Self { include_tree, include_screenshot, max_nodes, max_depth, window: None, include_menu_bar: false }
}
#[must_use]
pub const fn with_window(mut self, window: Option<u64>) -> Self {
self.window = window;
self
}
#[must_use]
pub const fn with_menu_bar(mut self, yes: bool) -> Self {
self.include_menu_bar = yes;
self
}
}
#[derive(Debug, Clone)]
#[non_exhaustive]
pub struct Capture<H> {
pub window: WindowInfo,
pub nodes: Vec<RawNode<H>>,
pub truncated: bool,
pub focused: Option<usize>,
pub selected_text: Option<String>,
pub screenshot: Option<RawImage>,
pub notes: Vec<String>,
}
impl<H> Capture<H> {
#[must_use]
pub const fn new(window: WindowInfo) -> Self {
Self {
window,
nodes: Vec::new(),
truncated: false,
focused: None,
selected_text: None,
screenshot: None,
notes: Vec::new(),
}
}
}
pub trait Backend {
type Handle: Clone;
fn list_apps(&self) -> Result<Vec<AppInfo>>;
fn launch_app(&self, app: &AppInfo) -> Result<AppInfo> {
Err(Error::Unsupported(format!("launching {} is not supported by this backend", app.name)))
}
fn permissions(&self) -> Permissions {
Permissions::GRANTED
}
fn request_permissions(&self) -> Permissions {
self.permissions()
}
fn list_windows(&self, app: &AppInfo) -> Result<Vec<WindowInfo>> {
let _ = app;
Err(Error::Unsupported("listing windows".into()))
}
fn capture(&self, app: &AppInfo, request: &CaptureRequest) -> Result<Capture<Self::Handle>>;
fn perform_action(&self, element: &Self::Handle, action: &str) -> Result<()>;
fn set_value(&self, element: &Self::Handle, value: &str) -> Result<()>;
fn value(&self, element: &Self::Handle) -> Result<Option<String>> {
let _ = element;
Err(Error::Unsupported("reading element values".into()))
}
fn select_text(&self, element: &Self::Handle, value: &str, range: Range<usize>) -> Result<()> {
let _ = (element, value, range);
Err(Error::Unsupported("text selection".into()))
}
fn insert_text(&self, app: &AppInfo, text: &str) -> Result<bool> {
let _ = (app, text);
Ok(false)
}
fn click(&self, target: InputTarget<'_>, point: Point, button: MouseButton, count: u32) -> Result<()>;
fn scroll(&self, target: InputTarget<'_>, point: Point, dx: f64, dy: f64) -> Result<()>;
fn drag(&self, target: InputTarget<'_>, from: Point, to: Point) -> Result<()>;
fn type_text(&self, target: InputTarget<'_>, text: &str) -> Result<()>;
fn press_key(&self, target: InputTarget<'_>, chord: &KeyChord) -> Result<()>;
}