1use rustenium_bidi_commands::browsing_context::types::BrowsingContext;
2use rustenium_bidi_commands::input::commands::{
3 InputCommand, PerformActions, PerformActionsParameters, InputPerformActionsMethod,
4};
5use rustenium_bidi_commands::input::types::{
6 PointerSourceActions, PointerSourceAction, PointerDownAction, PointerUpAction,
7 PointerMoveAction, SourceActions, PointerEnum, PointerDownEnum, PointerUpEnum,
8 PointerMoveEnum, PointerCommonProperties, PointerParameters, PointerType, Origin,
9};
10use rustenium_bidi_commands::CommandData;
11use rustenium_core::Session;
12use rustenium_core::transport::ConnectionTransport;
13use crate::error::InputError;
14use std::sync::Arc;
15use tokio::sync::Mutex;
16use crate::input::mouse::Point;
17use super::{FINGER_ID_PREFIX};
18
19#[derive(Debug, Clone, Default)]
21pub struct TouchMoveOptions {
22 pub origin: Option<Origin>,
24}
25
26pub struct TouchHandle<OT: ConnectionTransport> {
31 session: Arc<Mutex<Session<OT>>>,
32 touchscreen: Arc<Touchscreen<OT>>,
33 id: usize,
34 bidi_id: String,
35 position: Arc<Mutex<Point>>,
36 started: Arc<Mutex<bool>>,
37 properties: PointerCommonProperties,
38}
39
40impl<OT: ConnectionTransport> TouchHandle<OT> {
41 pub(crate) fn new(
42 session: Arc<Mutex<Session<OT>>>,
43 touchscreen: Arc<Touchscreen<OT>>,
44 id: usize,
45 x: f64,
46 y: f64,
47 ) -> Self {
48 let properties = PointerCommonProperties {
49 width: Some(1), height: Some(1), pressure: Some(0.5),
52 tangential_pressure: None,
53 twist: None,
54 altitude_angle: Some(std::f64::consts::PI / 2.0),
55 azimuth_angle: None,
56 };
57
58 Self {
59 session,
60 touchscreen,
61 id,
62 bidi_id: format!("{}_{}", FINGER_ID_PREFIX, id),
63 position: Arc::new(Mutex::new(Point { x: x.round(), y: y.round() })),
64 started: Arc::new(Mutex::new(false)),
65 properties,
66 }
67 }
68
69 pub async fn start(
77 &self,
78 context: &BrowsingContext,
79 options: Option<TouchMoveOptions>,
80 ) -> Result<(), InputError> {
81 let mut started = self.started.lock().await;
82 if *started {
83 return Err(InputError::TouchAlreadyStarted);
84 }
85
86 let options = options.unwrap_or_default();
87 let position = *self.position.lock().await;
88
89 let command = InputCommand::PerformActions(PerformActions {
90 method: InputPerformActionsMethod::InputPerformActions,
91 params: PerformActionsParameters {
92 context: context.clone(),
93 actions: vec![SourceActions::PointerSourceActions(PointerSourceActions {
94 r#type: PointerEnum::Pointer,
95 id: self.bidi_id.clone(),
96 parameters: Some(PointerParameters {
97 pointer_type: Some(PointerType::Touch),
98 }),
99 actions: vec![
100 PointerSourceAction::PointerMoveAction(PointerMoveAction {
101 r#type: PointerMoveEnum::PointerMove,
102 x: position.x,
103 y: position.y,
104 duration: None,
105 origin: options.origin,
106 pointer_common_properties: PointerCommonProperties {
107 width: None,
108 height: None,
109 pressure: None,
110 tangential_pressure: None,
111 twist: None,
112 altitude_angle: None,
113 azimuth_angle: None,
114 },
115 }),
116 PointerSourceAction::PointerDownAction(PointerDownAction {
117 r#type: PointerDownEnum::PointerDown,
118 button: 0,
119 pointer_common_properties: self.properties.clone(),
120 }),
121 ],
122 })],
123 },
124 });
125
126 let mut session = self.session.lock().await;
127 session.send(CommandData::InputCommand(command))
128 .await
129 .map_err(|e| InputError::CommandResultError(rustenium_core::error::CommandResultError::SessionSendError(e)))?;
130 *started = true;
131 Ok(())
132 }
133
134 pub async fn move_to(
144 &self,
145 x: f64,
146 y: f64,
147 context: &BrowsingContext,
148 ) -> Result<(), InputError> {
149 let new_position = Point { x: x.round(), y: y.round() };
150
151 let command = InputCommand::PerformActions(PerformActions {
152 method: InputPerformActionsMethod::InputPerformActions,
153 params: PerformActionsParameters {
154 context: context.clone(),
155 actions: vec![SourceActions::PointerSourceActions(PointerSourceActions {
156 r#type: PointerEnum::Pointer,
157 id: self.bidi_id.clone(),
158 parameters: Some(PointerParameters {
159 pointer_type: Some(PointerType::Touch),
160 }),
161 actions: vec![PointerSourceAction::PointerMoveAction(PointerMoveAction {
162 r#type: PointerMoveEnum::PointerMove,
163 x: new_position.x,
164 y: new_position.y,
165 duration: None,
166 origin: None,
167 pointer_common_properties: self.properties.clone(),
168 })],
169 })],
170 },
171 });
172
173 *self.position.lock().await = new_position;
174
175 let mut session = self.session.lock().await;
176 session.send(CommandData::InputCommand(command))
177 .await
178 .map_err(|e| InputError::CommandResultError(rustenium_core::error::CommandResultError::SessionSendError(e)))?;
179 Ok(())
180 }
181
182 pub async fn end(&self, context: &BrowsingContext) -> Result<(), InputError> {
187 let command = InputCommand::PerformActions(PerformActions {
188 method: InputPerformActionsMethod::InputPerformActions,
189 params: PerformActionsParameters {
190 context: context.clone(),
191 actions: vec![SourceActions::PointerSourceActions(PointerSourceActions {
192 r#type: PointerEnum::Pointer,
193 id: self.bidi_id.clone(),
194 parameters: Some(PointerParameters {
195 pointer_type: Some(PointerType::Touch),
196 }),
197 actions: vec![PointerSourceAction::PointerUpAction(PointerUpAction {
198 r#type: PointerUpEnum::PointerUp,
199 button: 0,
200 })],
201 })],
202 },
203 });
204
205 let mut session = self.session.lock().await;
206 session.send(CommandData::InputCommand(command))
207 .await
208 .map_err(|e| InputError::CommandResultError(rustenium_core::error::CommandResultError::SessionSendError(e)))?;
209
210 self.touchscreen.remove_handle(self.id).await;
212 Ok(())
213 }
214}
215
216pub struct Touchscreen<OT: ConnectionTransport> {
248 session: Arc<Mutex<Session<OT>>>,
249 touches: Arc<Mutex<Vec<usize>>>,
250 id_counter: Arc<Mutex<usize>>,
251}
252
253impl<OT: ConnectionTransport> Touchscreen<OT> {
254 pub fn new(session: Arc<Mutex<Session<OT>>>) -> Self {
256 Self {
257 session,
258 touches: Arc::new(Mutex::new(Vec::new())),
259 id_counter: Arc::new(Mutex::new(0)),
260 }
261 }
262
263 pub async fn touch_start(
277 self: &Arc<Self>,
278 x: f64,
279 y: f64,
280 context: &BrowsingContext,
281 options: Option<TouchMoveOptions>,
282 ) -> Result<TouchHandle<OT>, InputError> {
283 let mut counter = self.id_counter.lock().await;
284 let id = *counter;
285 *counter += 1;
286 drop(counter);
287
288 let touch = TouchHandle::new(
289 self.session.clone(),
290 self.clone(),
291 id,
292 x,
293 y,
294 );
295
296 touch.start(context, options).await?;
297
298 let mut touches = self.touches.lock().await;
299 touches.push(id);
300
301 Ok(touch)
302 }
303
304 pub(crate) async fn remove_handle(&self, id: usize) {
306 let mut touches = self.touches.lock().await;
307 touches.retain(|&touch_id| touch_id != id);
308 }
309}
310
311impl<OT: ConnectionTransport> Clone for Touchscreen<OT> {
312 fn clone(&self) -> Self {
313 Self {
314 session: self.session.clone(),
315 touches: self.touches.clone(),
316 id_counter: self.id_counter.clone(),
317 }
318 }
319}