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)]
71pub struct Mouse {
72 connection: Arc<CdpConnection>,
74 session_id: String,
76 state: Mutex<MouseState>,
78}
79
80impl Mouse {
81 pub(crate) fn new(connection: Arc<CdpConnection>, session_id: String) -> Self {
83 Self {
84 connection,
85 session_id,
86 state: Mutex::new(MouseState::new()),
87 }
88 }
89
90 pub fn move_(&self, x: f64, y: f64) -> MoveBuilder<'_> {
94 MoveBuilder {
95 mouse: self,
96 x,
97 y,
98 steps: 1,
99 }
100 }
101
102 pub fn click(&self, x: f64, y: f64) -> ClickBuilder<'_> {
106 ClickBuilder {
107 mouse: self,
108 x,
109 y,
110 button: MouseButton::Left,
111 click_count: 1,
112 delay: None,
113 }
114 }
115
116 #[instrument(level = "debug", skip(self), fields(x = x, y = y))]
118 pub async fn dblclick(&self, x: f64, y: f64) -> Result<(), LocatorError> {
119 debug!("Double-clicking at ({}, {})", x, y);
120
121 self.move_(x, y).send().await?;
123 self.down_internal(MouseButton::Left, 1).await?;
124 self.up_internal(MouseButton::Left, 1).await?;
125
126 self.down_internal(MouseButton::Left, 2).await?;
128 self.up_internal(MouseButton::Left, 2).await?;
129
130 Ok(())
131 }
132
133 pub fn down(&self) -> DownBuilder<'_> {
137 DownBuilder {
138 mouse: self,
139 button: MouseButton::Left,
140 click_count: 1,
141 }
142 }
143
144 pub fn up(&self) -> UpBuilder<'_> {
148 UpBuilder {
149 mouse: self,
150 button: MouseButton::Left,
151 click_count: 1,
152 }
153 }
154
155 #[instrument(level = "debug", skip(self), fields(delta_x = delta_x, delta_y = delta_y))]
157 pub async fn wheel(&self, delta_x: f64, delta_y: f64) -> Result<(), LocatorError> {
158 let state = self.state.lock().await;
159 let x = state.x;
160 let y = state.y;
161 drop(state);
162
163 debug!("Mouse wheel at ({}, {}): delta=({}, {})", x, y, delta_x, delta_y);
164
165 let params = DispatchMouseWheelParams {
166 event_type: MouseEventType::MouseWheel,
167 x,
168 y,
169 delta_x,
170 delta_y,
171 modifiers: None,
172 pointer_type: None,
173 };
174
175 self.connection
176 .send_command::<_, serde_json::Value>(
177 "Input.dispatchMouseEvent",
178 Some(params),
179 Some(&self.session_id),
180 )
181 .await?;
182
183 Ok(())
184 }
185
186 async fn move_internal(&self, x: f64, y: f64, steps: u32) -> Result<(), LocatorError> {
188 let (start_x, start_y) = {
189 let state = self.state.lock().await;
190 (state.x, state.y)
191 };
192
193 if steps <= 1 {
194 self.dispatch_move(x, y).await?;
196 } else {
197 for i in 1..=steps {
199 let progress = f64::from(i) / f64::from(steps);
200 let current_x = start_x + (x - start_x) * progress;
201 let current_y = start_y + (y - start_y) * progress;
202 self.dispatch_move(current_x, current_y).await?;
203 }
204 }
205
206 {
208 let mut state = self.state.lock().await;
209 state.x = x;
210 state.y = y;
211 }
212
213 Ok(())
214 }
215
216 async fn dispatch_move(&self, x: f64, y: f64) -> Result<(), LocatorError> {
218 let params = DispatchMouseEventParams::mouse_move(x, y);
219
220 self.connection
221 .send_command::<_, serde_json::Value>(
222 "Input.dispatchMouseEvent",
223 Some(params),
224 Some(&self.session_id),
225 )
226 .await?;
227
228 Ok(())
229 }
230
231 async fn down_internal(
233 &self,
234 button: MouseButton,
235 click_count: i32,
236 ) -> Result<(), LocatorError> {
237 let (x, y) = {
238 let state = self.state.lock().await;
239 (state.x, state.y)
240 };
241
242 debug!("Mouse down at ({}, {}), button={:?}, count={}", x, y, button, click_count);
243
244 let mut params = DispatchMouseEventParams::mouse_down(x, y, button);
245 params.click_count = Some(click_count);
246
247 self.connection
248 .send_command::<_, serde_json::Value>(
249 "Input.dispatchMouseEvent",
250 Some(params),
251 Some(&self.session_id),
252 )
253 .await?;
254
255 {
257 let mut state = self.state.lock().await;
258 state.button = Some(button);
259 }
260
261 Ok(())
262 }
263
264 async fn up_internal(&self, button: MouseButton, click_count: i32) -> Result<(), LocatorError> {
266 let (x, y) = {
267 let state = self.state.lock().await;
268 (state.x, state.y)
269 };
270
271 debug!("Mouse up at ({}, {}), button={:?}, count={}", x, y, button, click_count);
272
273 let mut params = DispatchMouseEventParams::mouse_up(x, y, button);
274 params.click_count = Some(click_count);
275
276 self.connection
277 .send_command::<_, serde_json::Value>(
278 "Input.dispatchMouseEvent",
279 Some(params),
280 Some(&self.session_id),
281 )
282 .await?;
283
284 {
286 let mut state = self.state.lock().await;
287 state.button = None;
288 }
289
290 Ok(())
291 }
292}
293
294#[derive(Debug)]
296pub struct MoveBuilder<'a> {
297 mouse: &'a Mouse,
298 x: f64,
299 y: f64,
300 steps: u32,
301}
302
303impl MoveBuilder<'_> {
304 #[must_use]
308 pub fn steps(mut self, steps: u32) -> Self {
309 self.steps = steps.max(1);
310 self
311 }
312
313 #[instrument(level = "debug", skip(self), fields(x = self.x, y = self.y, steps = self.steps))]
315 pub async fn send(self) -> Result<(), LocatorError> {
316 debug!("Moving mouse to ({}, {}) in {} steps", self.x, self.y, self.steps);
317 self.mouse.move_internal(self.x, self.y, self.steps).await
318 }
319}
320
321#[derive(Debug)]
323pub struct ClickBuilder<'a> {
324 mouse: &'a Mouse,
325 x: f64,
326 y: f64,
327 button: MouseButton,
328 click_count: i32,
329 delay: Option<Duration>,
330}
331
332impl ClickBuilder<'_> {
333 #[must_use]
337 pub fn button(mut self, button: MouseButton) -> Self {
338 self.button = button;
339 self
340 }
341
342 #[must_use]
346 pub fn click_count(mut self, count: i32) -> Self {
347 self.click_count = count;
348 self
349 }
350
351 #[must_use]
353 pub fn delay(mut self, delay: Duration) -> Self {
354 self.delay = Some(delay);
355 self
356 }
357
358 #[instrument(level = "debug", skip(self), fields(x = self.x, y = self.y, button = ?self.button))]
360 pub async fn send(self) -> Result<(), LocatorError> {
361 debug!("Clicking at ({}, {}), button={:?}", self.x, self.y, self.button);
362
363 self.mouse.move_(self.x, self.y).send().await?;
365
366 self.mouse.down_internal(self.button, self.click_count).await?;
368
369 if let Some(delay) = self.delay {
370 tokio::time::sleep(delay).await;
371 }
372
373 self.mouse.up_internal(self.button, self.click_count).await?;
374
375 Ok(())
376 }
377}
378
379#[derive(Debug)]
381pub struct DownBuilder<'a> {
382 mouse: &'a Mouse,
383 button: MouseButton,
384 click_count: i32,
385}
386
387impl DownBuilder<'_> {
388 #[must_use]
390 pub fn button(mut self, button: MouseButton) -> Self {
391 self.button = button;
392 self
393 }
394
395 #[must_use]
397 pub fn click_count(mut self, count: i32) -> Self {
398 self.click_count = count;
399 self
400 }
401
402 #[instrument(level = "debug", skip(self), fields(button = ?self.button))]
404 pub async fn send(self) -> Result<(), LocatorError> {
405 self.mouse.down_internal(self.button, self.click_count).await
406 }
407}
408
409#[derive(Debug)]
411pub struct UpBuilder<'a> {
412 mouse: &'a Mouse,
413 button: MouseButton,
414 click_count: i32,
415}
416
417impl UpBuilder<'_> {
418 #[must_use]
420 pub fn button(mut self, button: MouseButton) -> Self {
421 self.button = button;
422 self
423 }
424
425 #[must_use]
427 pub fn click_count(mut self, count: i32) -> Self {
428 self.click_count = count;
429 self
430 }
431
432 #[instrument(level = "debug", skip(self), fields(button = ?self.button))]
434 pub async fn send(self) -> Result<(), LocatorError> {
435 self.mouse.up_internal(self.button, self.click_count).await
436 }
437}