1use std::sync::Arc;
6use std::time::Duration;
7
8use tokio::sync::Mutex;
9use tracing::{debug, instrument};
10use viewpoint_cdp::CdpConnection;
11use viewpoint_cdp::protocol::input::{
12 DispatchMouseEventParams, DispatchMouseWheelParams, MouseButton, MouseEventType,
13};
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!(
164 "Mouse wheel at ({}, {}): delta=({}, {})",
165 x, y, delta_x, delta_y
166 );
167
168 let params = DispatchMouseWheelParams {
169 event_type: MouseEventType::MouseWheel,
170 x,
171 y,
172 delta_x,
173 delta_y,
174 modifiers: None,
175 pointer_type: None,
176 };
177
178 self.connection
179 .send_command::<_, serde_json::Value>(
180 "Input.dispatchMouseEvent",
181 Some(params),
182 Some(&self.session_id),
183 )
184 .await?;
185
186 Ok(())
187 }
188
189 async fn move_internal(&self, x: f64, y: f64, steps: u32) -> Result<(), LocatorError> {
191 let (start_x, start_y) = {
192 let state = self.state.lock().await;
193 (state.x, state.y)
194 };
195
196 if steps <= 1 {
197 self.dispatch_move(x, y).await?;
199 } else {
200 for i in 1..=steps {
202 let progress = f64::from(i) / f64::from(steps);
203 let current_x = start_x + (x - start_x) * progress;
204 let current_y = start_y + (y - start_y) * progress;
205 self.dispatch_move(current_x, current_y).await?;
206 }
207 }
208
209 {
211 let mut state = self.state.lock().await;
212 state.x = x;
213 state.y = y;
214 }
215
216 Ok(())
217 }
218
219 async fn dispatch_move(&self, x: f64, y: f64) -> Result<(), LocatorError> {
221 let params = DispatchMouseEventParams::mouse_move(x, y);
222
223 self.connection
224 .send_command::<_, serde_json::Value>(
225 "Input.dispatchMouseEvent",
226 Some(params),
227 Some(&self.session_id),
228 )
229 .await?;
230
231 Ok(())
232 }
233
234 async fn down_internal(
236 &self,
237 button: MouseButton,
238 click_count: i32,
239 ) -> Result<(), LocatorError> {
240 let (x, y) = {
241 let state = self.state.lock().await;
242 (state.x, state.y)
243 };
244
245 debug!(
246 "Mouse down at ({}, {}), button={:?}, count={}",
247 x, y, button, click_count
248 );
249
250 let mut params = DispatchMouseEventParams::mouse_down(x, y, button);
251 params.click_count = Some(click_count);
252
253 self.connection
254 .send_command::<_, serde_json::Value>(
255 "Input.dispatchMouseEvent",
256 Some(params),
257 Some(&self.session_id),
258 )
259 .await?;
260
261 {
263 let mut state = self.state.lock().await;
264 state.button = Some(button);
265 }
266
267 Ok(())
268 }
269
270 async fn up_internal(&self, button: MouseButton, click_count: i32) -> Result<(), LocatorError> {
272 let (x, y) = {
273 let state = self.state.lock().await;
274 (state.x, state.y)
275 };
276
277 debug!(
278 "Mouse up at ({}, {}), button={:?}, count={}",
279 x, y, button, click_count
280 );
281
282 let mut params = DispatchMouseEventParams::mouse_up(x, y, button);
283 params.click_count = Some(click_count);
284
285 self.connection
286 .send_command::<_, serde_json::Value>(
287 "Input.dispatchMouseEvent",
288 Some(params),
289 Some(&self.session_id),
290 )
291 .await?;
292
293 {
295 let mut state = self.state.lock().await;
296 state.button = None;
297 }
298
299 Ok(())
300 }
301}
302
303#[derive(Debug)]
305pub struct MoveBuilder<'a> {
306 mouse: &'a Mouse,
307 x: f64,
308 y: f64,
309 steps: u32,
310}
311
312impl MoveBuilder<'_> {
313 #[must_use]
317 pub fn steps(mut self, steps: u32) -> Self {
318 self.steps = steps.max(1);
319 self
320 }
321
322 #[instrument(level = "debug", skip(self), fields(x = self.x, y = self.y, steps = self.steps))]
324 pub async fn send(self) -> Result<(), LocatorError> {
325 debug!(
326 "Moving mouse to ({}, {}) in {} steps",
327 self.x, self.y, self.steps
328 );
329 self.mouse.move_internal(self.x, self.y, self.steps).await
330 }
331}
332
333#[derive(Debug)]
335pub struct ClickBuilder<'a> {
336 mouse: &'a Mouse,
337 x: f64,
338 y: f64,
339 button: MouseButton,
340 click_count: i32,
341 delay: Option<Duration>,
342}
343
344impl ClickBuilder<'_> {
345 #[must_use]
349 pub fn button(mut self, button: MouseButton) -> Self {
350 self.button = button;
351 self
352 }
353
354 #[must_use]
358 pub fn click_count(mut self, count: i32) -> Self {
359 self.click_count = count;
360 self
361 }
362
363 #[must_use]
365 pub fn delay(mut self, delay: Duration) -> Self {
366 self.delay = Some(delay);
367 self
368 }
369
370 #[instrument(level = "debug", skip(self), fields(x = self.x, y = self.y, button = ?self.button))]
372 pub async fn send(self) -> Result<(), LocatorError> {
373 debug!(
374 "Clicking at ({}, {}), button={:?}",
375 self.x, self.y, self.button
376 );
377
378 self.mouse.move_(self.x, self.y).send().await?;
380
381 self.mouse
383 .down_internal(self.button, self.click_count)
384 .await?;
385
386 if let Some(delay) = self.delay {
387 tokio::time::sleep(delay).await;
388 }
389
390 self.mouse
391 .up_internal(self.button, self.click_count)
392 .await?;
393
394 Ok(())
395 }
396}
397
398#[derive(Debug)]
400pub struct DownBuilder<'a> {
401 mouse: &'a Mouse,
402 button: MouseButton,
403 click_count: i32,
404}
405
406impl DownBuilder<'_> {
407 #[must_use]
409 pub fn button(mut self, button: MouseButton) -> Self {
410 self.button = button;
411 self
412 }
413
414 #[must_use]
416 pub fn click_count(mut self, count: i32) -> Self {
417 self.click_count = count;
418 self
419 }
420
421 #[instrument(level = "debug", skip(self), fields(button = ?self.button))]
423 pub async fn send(self) -> Result<(), LocatorError> {
424 self.mouse
425 .down_internal(self.button, self.click_count)
426 .await
427 }
428}
429
430#[derive(Debug)]
432pub struct UpBuilder<'a> {
433 mouse: &'a Mouse,
434 button: MouseButton,
435 click_count: i32,
436}
437
438impl UpBuilder<'_> {
439 #[must_use]
441 pub fn button(mut self, button: MouseButton) -> Self {
442 self.button = button;
443 self
444 }
445
446 #[must_use]
448 pub fn click_count(mut self, count: i32) -> Self {
449 self.click_count = count;
450 self
451 }
452
453 #[instrument(level = "debug", skip(self), fields(button = ?self.button))]
455 pub async fn send(self) -> Result<(), LocatorError> {
456 self.mouse.up_internal(self.button, self.click_count).await
457 }
458}