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}