computer_use/platform/linux/
mod.rs1mod a11y;
2mod apps;
3mod input;
4mod keys;
5mod screenshot;
6mod session;
7
8use std::ops::Range;
9use std::sync::Arc;
10
11use zbus::blocking::Connection;
12
13use self::screenshot::Shot;
14use self::session::SessionEnv;
15
16use crate::app::AppInfo;
17use crate::backend::{Backend, Capture, CaptureRequest, InputTarget, MouseButton};
18use crate::error::Result;
19use crate::geometry::Point;
20use crate::keys::KeyChord;
21
22#[derive(Debug, Clone, PartialEq, Eq, Hash)]
26pub struct LinuxElement {
27 bus: Arc<str>,
28 path: Arc<str>,
29}
30
31impl LinuxElement {
32 pub(crate) fn new(bus: &str, path: &str) -> Self {
33 Self { bus: bus.into(), path: path.into() }
34 }
35
36 #[must_use]
38 pub fn bus_name(&self) -> &str {
39 &self.bus
40 }
41
42 #[must_use]
44 pub fn object_path(&self) -> &str {
45 &self.path
46 }
47}
48
49#[derive(Debug, Clone)]
51pub struct LinuxBackend {
52 conn: Connection,
53 session: Arc<SessionEnv>,
54}
55
56impl LinuxBackend {
57 pub fn new() -> Result<Self> {
67 let session = SessionEnv::discover();
68 Ok(Self { conn: a11y::connect(&session)?, session: Arc::new(session) })
69 }
70}
71
72impl Backend for LinuxBackend {
73 type Handle = LinuxElement;
74
75 fn list_apps(&self) -> Result<Vec<AppInfo>> {
76 apps::list_apps(&self.conn)
77 }
78
79 fn launch_app(&self, app: &AppInfo) -> Result<AppInfo> {
80 apps::launch_app(&self.conn, &self.session, app)
81 }
82
83 fn capture(&self, app: &AppInfo, request: &CaptureRequest) -> Result<Capture<Self::Handle>> {
84 let root = apps::find_running(&self.conn, app)?;
85 let window = a11y::main_window(&self.conn, &root, &app.name)?;
86 let mut capture = Capture::new(window.info);
87 if request.include_tree {
88 let walk = a11y::walk(&self.conn, &window.element, request.max_nodes, request.max_depth)?;
89 capture.nodes = walk.nodes;
90 capture.truncated = walk.truncated;
91 capture.focused = walk.focused;
92 capture.selected_text = walk.selected_text;
93 }
94 if request.include_screenshot {
95 match screenshot::capture(&self.session, capture.window.frame)? {
96 Shot::Captured(frame, image) => {
97 capture.window.frame = frame;
98 capture.screenshot = Some(image);
99 }
100 Shot::Omitted(note) => capture.notes.push(note),
101 }
102 }
103 Ok(capture)
104 }
105
106 fn perform_action(&self, element: &Self::Handle, action: &str) -> Result<()> {
107 a11y::perform_action(&self.conn, element, action)
108 }
109
110 fn set_value(&self, element: &Self::Handle, value: &str) -> Result<()> {
111 a11y::set_value(&self.conn, element, value)
112 }
113
114 fn value(&self, element: &Self::Handle) -> Result<Option<String>> {
115 a11y::value(&self.conn, element)
116 }
117
118 fn select_text(&self, element: &Self::Handle, value: &str, range: Range<usize>) -> Result<()> {
119 a11y::select_text(&self.conn, element, value, range)
120 }
121
122 fn click(&self, _target: InputTarget<'_>, point: Point, button: MouseButton, count: u32) -> Result<()> {
123 input::click(&self.conn, point, button, count)
124 }
125
126 fn scroll(&self, _target: InputTarget<'_>, point: Point, dx: f64, dy: f64) -> Result<()> {
127 input::scroll(&self.conn, point, dx, dy)
128 }
129
130 fn drag(&self, _target: InputTarget<'_>, from: Point, to: Point) -> Result<()> {
131 input::drag(&self.conn, from, to)
132 }
133
134 fn type_text(&self, _target: InputTarget<'_>, text: &str) -> Result<()> {
135 input::type_text(&self.conn, text)
136 }
137
138 fn press_key(&self, _target: InputTarget<'_>, chord: &KeyChord) -> Result<()> {
139 input::press_key(&self.conn, chord)
140 }
141}