Skip to main content

rustenium/input/bidi/
mouse.rs

1use rustenium_bidi_definitions::browsing_context::types::BrowsingContext;
2use rustenium_bidi_definitions::input::command_builders::{PerformActionsBuilder, ReleaseActionsBuilder};
3use rustenium_bidi_definitions::input::type_builders::{
4    PointerSourceActionsBuilder, PointerDownActionBuilder, PointerUpActionBuilder,
5    PointerMoveActionBuilder, PointerCommonPropertiesBuilder, WheelSourceActionsBuilder,
6    WheelScrollActionBuilder, PauseActionBuilder,
7};
8use rustenium_bidi_definitions::input::types::{
9    PointerSourceActionsType, PointerDownActionType, PointerUpActionType, PointerMoveActionType,
10    WheelSourceActionsType, WheelScrollActionType, PauseActionType,
11};
12use rustenium_core::BidiSession;
13use rustenium_core::transport::ConnectionTransport;
14use crate::error::bidi::InputError;
15use std::sync::Arc;
16use tokio::sync::Mutex;
17
18use super::MOUSE_ID;
19use super::WHEEL_ID;
20use crate::input::mouse::{Mouse, MouseMoveOptions, MouseClickOptions, MouseOptions, MouseWheelOptions, MouseButton, Point};
21
22pub struct BidiMouse<OT: ConnectionTransport> {
23    session: Arc<Mutex<BidiSession<OT>>>,
24    last_move_point: Arc<Mutex<Point>>,
25}
26
27impl<OT: ConnectionTransport> BidiMouse<OT> {
28    pub fn new(session: Arc<Mutex<BidiSession<OT>>>) -> Self {
29        Self {
30            session,
31            last_move_point: Arc::new(Mutex::new(Point::default())),
32        }
33    }
34
35    pub async fn reset(&self, context: &BrowsingContext) -> Result<(), InputError> {
36        *self.last_move_point.lock().await = Point::default();
37
38        let command = ReleaseActionsBuilder::default()
39            .context(context.to_owned())
40            .build().unwrap();
41
42        self.session.lock().await.send(command).await
43            .map_err(|e| InputError::CommandResultError(rustenium_core::error::CommandResultError::SessionSendError(e)))?;
44        Ok(())
45    }
46
47    pub async fn move_to(
48        &self,
49        point: Point,
50        context: &BrowsingContext,
51        options: MouseMoveOptions,
52    ) -> Result<(), InputError> {
53        let last_point = *self.last_move_point.lock().await;
54        let to = Point { x: point.x.round(), y: point.y.round() };
55
56        let steps = options.steps.unwrap_or(0);
57        let empty_props = PointerCommonPropertiesBuilder::default().build();
58
59        let mut pointer_actions = PointerSourceActionsBuilder::default()
60            .r#type(PointerSourceActionsType::Pointer)
61            .id(MOUSE_ID);
62
63        for i in 0..steps {
64            let progress = (i as f64) / (steps as f64);
65            pointer_actions = pointer_actions.action(
66                PointerMoveActionBuilder::default()
67                    .r#type(PointerMoveActionType::PointerMove)
68                    .x(last_point.x + (to.x - last_point.x) * progress)
69                    .y(last_point.y + (to.y - last_point.y) * progress)
70                    .pointer_common_properties(empty_props.clone())
71                    .build().unwrap()
72            );
73        }
74
75        let mut final_move = PointerMoveActionBuilder::default()
76            .r#type(PointerMoveActionType::PointerMove)
77            .x(to.x)
78            .y(to.y)
79            .pointer_common_properties(empty_props);
80
81        if let Some(origin) = options.origin {
82            final_move = final_move.origin(origin);
83        }
84
85        pointer_actions = pointer_actions.action(final_move.build().unwrap());
86
87        let command = PerformActionsBuilder::default()
88            .context(context.to_owned())
89            .action(pointer_actions.build().unwrap())
90            .build().unwrap();
91
92        *self.last_move_point.lock().await = to;
93
94        self.session.lock().await.send(command).await
95            .map_err(|e| InputError::CommandResultError(rustenium_core::error::CommandResultError::SessionSendError(e)))?;
96        Ok(())
97    }
98
99    pub async fn down(
100        &self,
101        context: &BrowsingContext,
102        options: MouseOptions,
103    ) -> Result<(), InputError> {
104        let button = options.button.unwrap_or(MouseButton::Left) as u64;
105
106        let command = PerformActionsBuilder::default()
107            .context(context.to_owned())
108            .action(
109                PointerSourceActionsBuilder::default()
110                    .r#type(PointerSourceActionsType::Pointer)
111                    .id(MOUSE_ID)
112                    .action(PointerDownActionBuilder::default()
113                        .r#type(PointerDownActionType::PointerDown)
114                        .button(button)
115                        .pointer_common_properties(PointerCommonPropertiesBuilder::default().build())
116                        .build().unwrap())
117                    .build().unwrap()
118            )
119            .build().unwrap();
120
121        self.session.lock().await.send(command).await
122            .map_err(|e| InputError::CommandResultError(rustenium_core::error::CommandResultError::SessionSendError(e)))?;
123        Ok(())
124    }
125
126    pub async fn up(
127        &self,
128        context: &BrowsingContext,
129        options: MouseOptions,
130    ) -> Result<(), InputError> {
131        let button = options.button.unwrap_or(MouseButton::Left) as u64;
132
133        let command = PerformActionsBuilder::default()
134            .context(context.to_owned())
135            .action(
136                PointerSourceActionsBuilder::default()
137                    .r#type(PointerSourceActionsType::Pointer)
138                    .id(MOUSE_ID)
139                    .action(PointerUpActionBuilder::default()
140                        .r#type(PointerUpActionType::PointerUp)
141                        .button(button)
142                        .build().unwrap())
143                    .build().unwrap()
144            )
145            .build().unwrap();
146
147        self.session.lock().await.send(command).await
148            .map_err(|e| InputError::CommandResultError(rustenium_core::error::CommandResultError::SessionSendError(e)))?;
149        Ok(())
150    }
151
152    pub async fn click(
153        &self,
154        point: Option<Point>,
155        context: &BrowsingContext,
156        options: MouseClickOptions,
157    ) -> Result<(), InputError> {
158        let button = options.button.unwrap_or(MouseButton::Left) as u64;
159        let count = options.count.unwrap_or(1);
160
161        let click_point = match point {
162            Some(p) => p,
163            None => *self.last_move_point.lock().await,
164        };
165
166        let pointer_down = PointerDownActionBuilder::default()
167            .r#type(PointerDownActionType::PointerDown)
168            .button(button)
169            .pointer_common_properties(PointerCommonPropertiesBuilder::default().build())
170            .build().unwrap();
171
172        let pointer_up = PointerUpActionBuilder::default()
173            .r#type(PointerUpActionType::PointerUp)
174            .button(button)
175            .build().unwrap();
176
177        let mut pointer_actions = PointerSourceActionsBuilder::default()
178            .r#type(PointerSourceActionsType::Pointer)
179            .id(MOUSE_ID)
180            .action(PointerMoveActionBuilder::default()
181                .r#type(PointerMoveActionType::PointerMove)
182                .x(click_point.x.round())
183                .y(click_point.y.round())
184                .pointer_common_properties(PointerCommonPropertiesBuilder::default().build())
185                .build().unwrap());
186
187        for _ in 1..count {
188            pointer_actions = pointer_actions
189                .action(pointer_down.clone())
190                .action(pointer_up.clone());
191        }
192
193        pointer_actions = pointer_actions.action(pointer_down);
194
195        if let Some(delay) = options.delay {
196            if delay > 0 {
197                pointer_actions = pointer_actions.action(PauseActionBuilder::default()
198                    .r#type(PauseActionType::Pause)
199                    .duration(delay)
200                    .build().unwrap());
201            }
202        }
203
204        pointer_actions = pointer_actions.action(pointer_up);
205
206        let command = PerformActionsBuilder::default()
207            .context(context.to_owned())
208            .action(pointer_actions.build().unwrap())
209            .build().unwrap();
210
211        self.session.lock().await.send(command).await
212            .map_err(|e| InputError::CommandResultError(rustenium_core::error::CommandResultError::SessionSendError(e)))?;
213        Ok(())
214    }
215
216    pub async fn wheel(
217        &self,
218        context: &BrowsingContext,
219        options: MouseWheelOptions,
220    ) -> Result<(), InputError> {
221        let last_point = *self.last_move_point.lock().await;
222
223        let command = PerformActionsBuilder::default()
224            .context(context.to_owned())
225            .action(
226                WheelSourceActionsBuilder::default()
227                    .r#type(WheelSourceActionsType::Wheel)
228                    .id(WHEEL_ID)
229                    .action(WheelScrollActionBuilder::default()
230                        .r#type(WheelScrollActionType::Scroll)
231                        .x(last_point.x as i64)
232                        .y(last_point.y as i64)
233                        .delta_x(options.delta_x.unwrap_or(0))
234                        .delta_y(options.delta_y.unwrap_or(0))
235                        .build().unwrap())
236                    .build().unwrap()
237            )
238            .build().unwrap();
239
240        self.session.lock().await.send(command).await
241            .map_err(|e| InputError::CommandResultError(rustenium_core::error::CommandResultError::SessionSendError(e)))?;
242        Ok(())
243    }
244}
245
246impl<OT: ConnectionTransport> Mouse for BidiMouse<OT> {
247    fn get_last_position(&self) -> Arc<Mutex<Point>> {
248        self.last_move_point.clone()
249    }
250
251    fn set_last_position(&self, point: Point) {
252        if let Ok(mut last_point) = self.last_move_point.try_lock() {
253            *last_point = point;
254        }
255    }
256
257    async fn reset(&self, context: &BrowsingContext) -> Result<(), InputError> {
258        Self::reset(self, context).await
259    }
260
261    async fn move_to(&self, point: Point, context: &BrowsingContext, options: MouseMoveOptions) -> Result<(), InputError> {
262        Self::move_to(self, point, context, options).await
263    }
264
265    async fn down(&self, context: &BrowsingContext, options: MouseOptions) -> Result<(), InputError> {
266        Self::down(self, context, options).await
267    }
268
269    async fn up(&self, context: &BrowsingContext, options: MouseOptions) -> Result<(), InputError> {
270        Self::up(self, context, options).await
271    }
272
273    async fn click(&self, point: Option<Point>, context: &BrowsingContext, options: MouseClickOptions) -> Result<(), InputError> {
274        Self::click(self, point, context, options).await
275    }
276
277    async fn wheel(&self, context: &BrowsingContext, options: MouseWheelOptions) -> Result<(), InputError> {
278        Self::wheel(self, context, options).await
279    }
280}