viewpoint_core/page/touchscreen/
mod.rs1use std::sync::Arc;
6use std::sync::atomic::{AtomicBool, AtomicI32, Ordering};
7
8use tracing::{debug, instrument};
9use viewpoint_cdp::CdpConnection;
10use viewpoint_cdp::protocol::emulation::SetTouchEmulationEnabledParams;
11use viewpoint_cdp::protocol::input::{DispatchTouchEventParams, TouchPoint};
12
13use crate::error::LocatorError;
14
15static TOUCH_ID_COUNTER: AtomicI32 = AtomicI32::new(0);
17
18#[derive(Debug)]
42pub struct Touchscreen {
43 connection: Arc<CdpConnection>,
45 session_id: String,
47 enabled: AtomicBool,
49}
50
51impl Touchscreen {
52 pub(crate) fn new(connection: Arc<CdpConnection>, session_id: String) -> Self {
54 Self {
55 connection,
56 session_id,
57 enabled: AtomicBool::new(false),
58 }
59 }
60
61 #[instrument(level = "debug", skip(self))]
85 pub async fn enable(&self) -> Result<(), LocatorError> {
86 self.enable_with_max_points(1).await
87 }
88
89 #[instrument(level = "debug", skip(self), fields(max_touch_points = max_touch_points))]
91 pub async fn enable_with_max_points(&self, max_touch_points: i32) -> Result<(), LocatorError> {
92 debug!(
93 "Enabling touch emulation with max_touch_points={}",
94 max_touch_points
95 );
96
97 self.connection
98 .send_command::<_, serde_json::Value>(
99 "Emulation.setTouchEmulationEnabled",
100 Some(SetTouchEmulationEnabledParams {
101 enabled: true,
102 max_touch_points: Some(max_touch_points),
103 }),
104 Some(&self.session_id),
105 )
106 .await?;
107
108 self.enabled.store(true, Ordering::SeqCst);
109 Ok(())
110 }
111
112 #[instrument(level = "debug", skip(self))]
114 pub async fn disable(&self) -> Result<(), LocatorError> {
115 debug!("Disabling touch emulation");
116
117 self.connection
118 .send_command::<_, serde_json::Value>(
119 "Emulation.setTouchEmulationEnabled",
120 Some(SetTouchEmulationEnabledParams {
121 enabled: false,
122 max_touch_points: None,
123 }),
124 Some(&self.session_id),
125 )
126 .await?;
127
128 self.enabled.store(false, Ordering::SeqCst);
129 Ok(())
130 }
131
132 pub fn is_enabled(&self) -> bool {
134 self.enabled.load(Ordering::SeqCst)
135 }
136
137 pub(crate) fn set_enabled(&self, enabled: bool) {
139 self.enabled.store(enabled, Ordering::SeqCst);
140 }
141
142 fn check_enabled(&self) -> Result<(), LocatorError> {
144 if !self.is_enabled() {
145 return Err(LocatorError::TouchNotEnabled);
146 }
147 Ok(())
148 }
149
150 #[instrument(level = "debug", skip(self), fields(x = x, y = y))]
176 pub async fn tap(&self, x: f64, y: f64) -> Result<(), LocatorError> {
177 self.check_enabled()?;
178 debug!("Tapping at ({}, {})", x, y);
179
180 let touch_id = TOUCH_ID_COUNTER.fetch_add(1, Ordering::SeqCst);
182
183 let mut touch_point = TouchPoint::new(x, y);
185 touch_point.id = Some(touch_id);
186
187 let start_params = DispatchTouchEventParams {
188 event_type: viewpoint_cdp::protocol::input::TouchEventType::TouchStart,
189 touch_points: vec![touch_point.clone()],
190 modifiers: None,
191 timestamp: None,
192 };
193
194 self.connection
195 .send_command::<_, serde_json::Value>(
196 "Input.dispatchTouchEvent",
197 Some(start_params),
198 Some(&self.session_id),
199 )
200 .await?;
201
202 let end_params = DispatchTouchEventParams {
204 event_type: viewpoint_cdp::protocol::input::TouchEventType::TouchEnd,
205 touch_points: vec![],
206 modifiers: None,
207 timestamp: None,
208 };
209
210 self.connection
211 .send_command::<_, serde_json::Value>(
212 "Input.dispatchTouchEvent",
213 Some(end_params),
214 Some(&self.session_id),
215 )
216 .await?;
217
218 Ok(())
219 }
220
221 #[instrument(level = "debug", skip(self), fields(x = x, y = y, modifiers = modifiers))]
227 pub async fn tap_with_modifiers(
228 &self,
229 x: f64,
230 y: f64,
231 modifiers: i32,
232 ) -> Result<(), LocatorError> {
233 self.check_enabled()?;
234 debug!("Tapping at ({}, {}) with modifiers {}", x, y, modifiers);
235
236 let touch_id = TOUCH_ID_COUNTER.fetch_add(1, Ordering::SeqCst);
237
238 let mut touch_point = TouchPoint::new(x, y);
239 touch_point.id = Some(touch_id);
240
241 let start_params = DispatchTouchEventParams {
243 event_type: viewpoint_cdp::protocol::input::TouchEventType::TouchStart,
244 touch_points: vec![touch_point],
245 modifiers: Some(modifiers),
246 timestamp: None,
247 };
248
249 self.connection
250 .send_command::<_, serde_json::Value>(
251 "Input.dispatchTouchEvent",
252 Some(start_params),
253 Some(&self.session_id),
254 )
255 .await?;
256
257 let end_params = DispatchTouchEventParams {
259 event_type: viewpoint_cdp::protocol::input::TouchEventType::TouchEnd,
260 touch_points: vec![],
261 modifiers: Some(modifiers),
262 timestamp: None,
263 };
264
265 self.connection
266 .send_command::<_, serde_json::Value>(
267 "Input.dispatchTouchEvent",
268 Some(end_params),
269 Some(&self.session_id),
270 )
271 .await?;
272
273 Ok(())
274 }
275}