viewpoint_core/page/touchscreen/
mod.rs1use std::sync::Arc;
6use std::sync::atomic::{AtomicBool, AtomicI32, Ordering};
7
8use tracing::{debug, instrument};
9use viewpoint_cdp::protocol::emulation::SetTouchEmulationEnabledParams;
10use viewpoint_cdp::protocol::input::{DispatchTouchEventParams, TouchPoint};
11use viewpoint_cdp::CdpConnection;
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!("Enabling touch emulation with max_touch_points={}", max_touch_points);
93
94 self.connection
95 .send_command::<_, serde_json::Value>(
96 "Emulation.setTouchEmulationEnabled",
97 Some(SetTouchEmulationEnabledParams {
98 enabled: true,
99 max_touch_points: Some(max_touch_points),
100 }),
101 Some(&self.session_id),
102 )
103 .await?;
104
105 self.enabled.store(true, Ordering::SeqCst);
106 Ok(())
107 }
108
109 #[instrument(level = "debug", skip(self))]
111 pub async fn disable(&self) -> Result<(), LocatorError> {
112 debug!("Disabling touch emulation");
113
114 self.connection
115 .send_command::<_, serde_json::Value>(
116 "Emulation.setTouchEmulationEnabled",
117 Some(SetTouchEmulationEnabledParams {
118 enabled: false,
119 max_touch_points: None,
120 }),
121 Some(&self.session_id),
122 )
123 .await?;
124
125 self.enabled.store(false, Ordering::SeqCst);
126 Ok(())
127 }
128
129 pub fn is_enabled(&self) -> bool {
131 self.enabled.load(Ordering::SeqCst)
132 }
133
134 pub(crate) fn set_enabled(&self, enabled: bool) {
136 self.enabled.store(enabled, Ordering::SeqCst);
137 }
138
139 fn check_enabled(&self) -> Result<(), LocatorError> {
141 if !self.is_enabled() {
142 return Err(LocatorError::TouchNotEnabled);
143 }
144 Ok(())
145 }
146
147 #[instrument(level = "debug", skip(self), fields(x = x, y = y))]
173 pub async fn tap(&self, x: f64, y: f64) -> Result<(), LocatorError> {
174 self.check_enabled()?;
175 debug!("Tapping at ({}, {})", x, y);
176
177 let touch_id = TOUCH_ID_COUNTER.fetch_add(1, Ordering::SeqCst);
179
180 let mut touch_point = TouchPoint::new(x, y);
182 touch_point.id = Some(touch_id);
183
184 let start_params = DispatchTouchEventParams {
185 event_type: viewpoint_cdp::protocol::input::TouchEventType::TouchStart,
186 touch_points: vec![touch_point.clone()],
187 modifiers: None,
188 timestamp: None,
189 };
190
191 self.connection
192 .send_command::<_, serde_json::Value>(
193 "Input.dispatchTouchEvent",
194 Some(start_params),
195 Some(&self.session_id),
196 )
197 .await?;
198
199 let end_params = DispatchTouchEventParams {
201 event_type: viewpoint_cdp::protocol::input::TouchEventType::TouchEnd,
202 touch_points: vec![],
203 modifiers: None,
204 timestamp: None,
205 };
206
207 self.connection
208 .send_command::<_, serde_json::Value>(
209 "Input.dispatchTouchEvent",
210 Some(end_params),
211 Some(&self.session_id),
212 )
213 .await?;
214
215 Ok(())
216 }
217
218 #[instrument(level = "debug", skip(self), fields(x = x, y = y, modifiers = modifiers))]
224 pub async fn tap_with_modifiers(
225 &self,
226 x: f64,
227 y: f64,
228 modifiers: i32,
229 ) -> Result<(), LocatorError> {
230 self.check_enabled()?;
231 debug!("Tapping at ({}, {}) with modifiers {}", x, y, modifiers);
232
233 let touch_id = TOUCH_ID_COUNTER.fetch_add(1, Ordering::SeqCst);
234
235 let mut touch_point = TouchPoint::new(x, y);
236 touch_point.id = Some(touch_id);
237
238 let start_params = DispatchTouchEventParams {
240 event_type: viewpoint_cdp::protocol::input::TouchEventType::TouchStart,
241 touch_points: vec![touch_point],
242 modifiers: Some(modifiers),
243 timestamp: None,
244 };
245
246 self.connection
247 .send_command::<_, serde_json::Value>(
248 "Input.dispatchTouchEvent",
249 Some(start_params),
250 Some(&self.session_id),
251 )
252 .await?;
253
254 let end_params = DispatchTouchEventParams {
256 event_type: viewpoint_cdp::protocol::input::TouchEventType::TouchEnd,
257 touch_points: vec![],
258 modifiers: Some(modifiers),
259 timestamp: None,
260 };
261
262 self.connection
263 .send_command::<_, serde_json::Value>(
264 "Input.dispatchTouchEvent",
265 Some(end_params),
266 Some(&self.session_id),
267 )
268 .await?;
269
270 Ok(())
271 }
272}