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))]
80 pub async fn enable(&self) -> Result<(), LocatorError> {
81 self.enable_with_max_points(1).await
82 }
83
84 #[instrument(level = "debug", skip(self), fields(max_touch_points = max_touch_points))]
86 pub async fn enable_with_max_points(&self, max_touch_points: i32) -> Result<(), LocatorError> {
87 debug!("Enabling touch emulation with max_touch_points={}", max_touch_points);
88
89 self.connection
90 .send_command::<_, serde_json::Value>(
91 "Emulation.setTouchEmulationEnabled",
92 Some(SetTouchEmulationEnabledParams {
93 enabled: true,
94 max_touch_points: Some(max_touch_points),
95 }),
96 Some(&self.session_id),
97 )
98 .await?;
99
100 self.enabled.store(true, Ordering::SeqCst);
101 Ok(())
102 }
103
104 #[instrument(level = "debug", skip(self))]
106 pub async fn disable(&self) -> Result<(), LocatorError> {
107 debug!("Disabling touch emulation");
108
109 self.connection
110 .send_command::<_, serde_json::Value>(
111 "Emulation.setTouchEmulationEnabled",
112 Some(SetTouchEmulationEnabledParams {
113 enabled: false,
114 max_touch_points: None,
115 }),
116 Some(&self.session_id),
117 )
118 .await?;
119
120 self.enabled.store(false, Ordering::SeqCst);
121 Ok(())
122 }
123
124 pub fn is_enabled(&self) -> bool {
126 self.enabled.load(Ordering::SeqCst)
127 }
128
129 pub(crate) fn set_enabled(&self, enabled: bool) {
131 self.enabled.store(enabled, Ordering::SeqCst);
132 }
133
134 fn check_enabled(&self) -> Result<(), LocatorError> {
136 if !self.is_enabled() {
137 return Err(LocatorError::TouchNotEnabled);
138 }
139 Ok(())
140 }
141
142 #[instrument(level = "debug", skip(self), fields(x = x, y = y))]
163 pub async fn tap(&self, x: f64, y: f64) -> Result<(), LocatorError> {
164 self.check_enabled()?;
165 debug!("Tapping at ({}, {})", x, y);
166
167 let touch_id = TOUCH_ID_COUNTER.fetch_add(1, Ordering::SeqCst);
169
170 let mut touch_point = TouchPoint::new(x, y);
172 touch_point.id = Some(touch_id);
173
174 let start_params = DispatchTouchEventParams {
175 event_type: viewpoint_cdp::protocol::input::TouchEventType::TouchStart,
176 touch_points: vec![touch_point.clone()],
177 modifiers: None,
178 timestamp: None,
179 };
180
181 self.connection
182 .send_command::<_, serde_json::Value>(
183 "Input.dispatchTouchEvent",
184 Some(start_params),
185 Some(&self.session_id),
186 )
187 .await?;
188
189 let end_params = DispatchTouchEventParams {
191 event_type: viewpoint_cdp::protocol::input::TouchEventType::TouchEnd,
192 touch_points: vec![],
193 modifiers: None,
194 timestamp: None,
195 };
196
197 self.connection
198 .send_command::<_, serde_json::Value>(
199 "Input.dispatchTouchEvent",
200 Some(end_params),
201 Some(&self.session_id),
202 )
203 .await?;
204
205 Ok(())
206 }
207
208 #[instrument(level = "debug", skip(self), fields(x = x, y = y, modifiers = modifiers))]
214 pub async fn tap_with_modifiers(
215 &self,
216 x: f64,
217 y: f64,
218 modifiers: i32,
219 ) -> Result<(), LocatorError> {
220 self.check_enabled()?;
221 debug!("Tapping at ({}, {}) with modifiers {}", x, y, modifiers);
222
223 let touch_id = TOUCH_ID_COUNTER.fetch_add(1, Ordering::SeqCst);
224
225 let mut touch_point = TouchPoint::new(x, y);
226 touch_point.id = Some(touch_id);
227
228 let start_params = DispatchTouchEventParams {
230 event_type: viewpoint_cdp::protocol::input::TouchEventType::TouchStart,
231 touch_points: vec![touch_point],
232 modifiers: Some(modifiers),
233 timestamp: None,
234 };
235
236 self.connection
237 .send_command::<_, serde_json::Value>(
238 "Input.dispatchTouchEvent",
239 Some(start_params),
240 Some(&self.session_id),
241 )
242 .await?;
243
244 let end_params = DispatchTouchEventParams {
246 event_type: viewpoint_cdp::protocol::input::TouchEventType::TouchEnd,
247 touch_points: vec![],
248 modifiers: Some(modifiers),
249 timestamp: None,
250 };
251
252 self.connection
253 .send_command::<_, serde_json::Value>(
254 "Input.dispatchTouchEvent",
255 Some(end_params),
256 Some(&self.session_id),
257 )
258 .await?;
259
260 Ok(())
261 }
262}