1use std::sync::Arc;
6use std::time::Duration;
7
8use tokio::sync::Mutex;
9use tracing::{debug, instrument};
10use viewpoint_cdp::protocol::input::{
11 DispatchMouseEventParams, DispatchMouseWheelParams, MouseButton, MouseEventType,
12};
13use viewpoint_cdp::CdpConnection;
14
15use crate::error::LocatorError;
16
17#[derive(Debug)]
19struct MouseState {
20 x: f64,
22 y: f64,
24 button: Option<MouseButton>,
26}
27
28impl MouseState {
29 fn new() -> Self {
30 Self {
31 x: 0.0,
32 y: 0.0,
33 button: None,
34 }
35 }
36}
37
38#[derive(Debug)]
65pub struct Mouse {
66 connection: Arc<CdpConnection>,
68 session_id: String,
70 state: Mutex<MouseState>,
72}
73
74impl Mouse {
75 pub(crate) fn new(connection: Arc<CdpConnection>, session_id: String) -> Self {
77 Self {
78 connection,
79 session_id,
80 state: Mutex::new(MouseState::new()),
81 }
82 }
83
84 pub fn move_(&self, x: f64, y: f64) -> MoveBuilder<'_> {
88 MoveBuilder {
89 mouse: self,
90 x,
91 y,
92 steps: 1,
93 }
94 }
95
96 pub fn click(&self, x: f64, y: f64) -> ClickBuilder<'_> {
100 ClickBuilder {
101 mouse: self,
102 x,
103 y,
104 button: MouseButton::Left,
105 click_count: 1,
106 delay: None,
107 }
108 }
109
110 #[instrument(level = "debug", skip(self), fields(x = x, y = y))]
112 pub async fn dblclick(&self, x: f64, y: f64) -> Result<(), LocatorError> {
113 debug!("Double-clicking at ({}, {})", x, y);
114
115 self.move_(x, y).send().await?;
117 self.down_internal(MouseButton::Left, 1).await?;
118 self.up_internal(MouseButton::Left, 1).await?;
119
120 self.down_internal(MouseButton::Left, 2).await?;
122 self.up_internal(MouseButton::Left, 2).await?;
123
124 Ok(())
125 }
126
127 pub fn down(&self) -> DownBuilder<'_> {
131 DownBuilder {
132 mouse: self,
133 button: MouseButton::Left,
134 click_count: 1,
135 }
136 }
137
138 pub fn up(&self) -> UpBuilder<'_> {
142 UpBuilder {
143 mouse: self,
144 button: MouseButton::Left,
145 click_count: 1,
146 }
147 }
148
149 #[instrument(level = "debug", skip(self), fields(delta_x = delta_x, delta_y = delta_y))]
151 pub async fn wheel(&self, delta_x: f64, delta_y: f64) -> Result<(), LocatorError> {
152 let state = self.state.lock().await;
153 let x = state.x;
154 let y = state.y;
155 drop(state);
156
157 debug!("Mouse wheel at ({}, {}): delta=({}, {})", x, y, delta_x, delta_y);
158
159 let params = DispatchMouseWheelParams {
160 event_type: MouseEventType::MouseWheel,
161 x,
162 y,
163 delta_x,
164 delta_y,
165 modifiers: None,
166 pointer_type: None,
167 };
168
169 self.connection
170 .send_command::<_, serde_json::Value>(
171 "Input.dispatchMouseEvent",
172 Some(params),
173 Some(&self.session_id),
174 )
175 .await?;
176
177 Ok(())
178 }
179
180 async fn move_internal(&self, x: f64, y: f64, steps: u32) -> Result<(), LocatorError> {
182 let (start_x, start_y) = {
183 let state = self.state.lock().await;
184 (state.x, state.y)
185 };
186
187 if steps <= 1 {
188 self.dispatch_move(x, y).await?;
190 } else {
191 for i in 1..=steps {
193 let progress = f64::from(i) / f64::from(steps);
194 let current_x = start_x + (x - start_x) * progress;
195 let current_y = start_y + (y - start_y) * progress;
196 self.dispatch_move(current_x, current_y).await?;
197 }
198 }
199
200 {
202 let mut state = self.state.lock().await;
203 state.x = x;
204 state.y = y;
205 }
206
207 Ok(())
208 }
209
210 async fn dispatch_move(&self, x: f64, y: f64) -> Result<(), LocatorError> {
212 let params = DispatchMouseEventParams::mouse_move(x, y);
213
214 self.connection
215 .send_command::<_, serde_json::Value>(
216 "Input.dispatchMouseEvent",
217 Some(params),
218 Some(&self.session_id),
219 )
220 .await?;
221
222 Ok(())
223 }
224
225 async fn down_internal(
227 &self,
228 button: MouseButton,
229 click_count: i32,
230 ) -> Result<(), LocatorError> {
231 let (x, y) = {
232 let state = self.state.lock().await;
233 (state.x, state.y)
234 };
235
236 debug!("Mouse down at ({}, {}), button={:?}, count={}", x, y, button, click_count);
237
238 let mut params = DispatchMouseEventParams::mouse_down(x, y, button);
239 params.click_count = Some(click_count);
240
241 self.connection
242 .send_command::<_, serde_json::Value>(
243 "Input.dispatchMouseEvent",
244 Some(params),
245 Some(&self.session_id),
246 )
247 .await?;
248
249 {
251 let mut state = self.state.lock().await;
252 state.button = Some(button);
253 }
254
255 Ok(())
256 }
257
258 async fn up_internal(&self, button: MouseButton, click_count: i32) -> Result<(), LocatorError> {
260 let (x, y) = {
261 let state = self.state.lock().await;
262 (state.x, state.y)
263 };
264
265 debug!("Mouse up at ({}, {}), button={:?}, count={}", x, y, button, click_count);
266
267 let mut params = DispatchMouseEventParams::mouse_up(x, y, button);
268 params.click_count = Some(click_count);
269
270 self.connection
271 .send_command::<_, serde_json::Value>(
272 "Input.dispatchMouseEvent",
273 Some(params),
274 Some(&self.session_id),
275 )
276 .await?;
277
278 {
280 let mut state = self.state.lock().await;
281 state.button = None;
282 }
283
284 Ok(())
285 }
286}
287
288#[derive(Debug)]
290pub struct MoveBuilder<'a> {
291 mouse: &'a Mouse,
292 x: f64,
293 y: f64,
294 steps: u32,
295}
296
297impl MoveBuilder<'_> {
298 #[must_use]
302 pub fn steps(mut self, steps: u32) -> Self {
303 self.steps = steps.max(1);
304 self
305 }
306
307 #[instrument(level = "debug", skip(self), fields(x = self.x, y = self.y, steps = self.steps))]
309 pub async fn send(self) -> Result<(), LocatorError> {
310 debug!("Moving mouse to ({}, {}) in {} steps", self.x, self.y, self.steps);
311 self.mouse.move_internal(self.x, self.y, self.steps).await
312 }
313}
314
315#[derive(Debug)]
317pub struct ClickBuilder<'a> {
318 mouse: &'a Mouse,
319 x: f64,
320 y: f64,
321 button: MouseButton,
322 click_count: i32,
323 delay: Option<Duration>,
324}
325
326impl ClickBuilder<'_> {
327 #[must_use]
331 pub fn button(mut self, button: MouseButton) -> Self {
332 self.button = button;
333 self
334 }
335
336 #[must_use]
340 pub fn click_count(mut self, count: i32) -> Self {
341 self.click_count = count;
342 self
343 }
344
345 #[must_use]
347 pub fn delay(mut self, delay: Duration) -> Self {
348 self.delay = Some(delay);
349 self
350 }
351
352 #[instrument(level = "debug", skip(self), fields(x = self.x, y = self.y, button = ?self.button))]
354 pub async fn send(self) -> Result<(), LocatorError> {
355 debug!("Clicking at ({}, {}), button={:?}", self.x, self.y, self.button);
356
357 self.mouse.move_(self.x, self.y).send().await?;
359
360 self.mouse.down_internal(self.button, self.click_count).await?;
362
363 if let Some(delay) = self.delay {
364 tokio::time::sleep(delay).await;
365 }
366
367 self.mouse.up_internal(self.button, self.click_count).await?;
368
369 Ok(())
370 }
371}
372
373#[derive(Debug)]
375pub struct DownBuilder<'a> {
376 mouse: &'a Mouse,
377 button: MouseButton,
378 click_count: i32,
379}
380
381impl DownBuilder<'_> {
382 #[must_use]
384 pub fn button(mut self, button: MouseButton) -> Self {
385 self.button = button;
386 self
387 }
388
389 #[must_use]
391 pub fn click_count(mut self, count: i32) -> Self {
392 self.click_count = count;
393 self
394 }
395
396 #[instrument(level = "debug", skip(self), fields(button = ?self.button))]
398 pub async fn send(self) -> Result<(), LocatorError> {
399 self.mouse.down_internal(self.button, self.click_count).await
400 }
401}
402
403#[derive(Debug)]
405pub struct UpBuilder<'a> {
406 mouse: &'a Mouse,
407 button: MouseButton,
408 click_count: i32,
409}
410
411impl UpBuilder<'_> {
412 #[must_use]
414 pub fn button(mut self, button: MouseButton) -> Self {
415 self.button = button;
416 self
417 }
418
419 #[must_use]
421 pub fn click_count(mut self, count: i32) -> Self {
422 self.click_count = count;
423 self
424 }
425
426 #[instrument(level = "debug", skip(self), fields(button = ?self.button))]
428 pub async fn send(self) -> Result<(), LocatorError> {
429 self.mouse.up_internal(self.button, self.click_count).await
430 }
431}