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)]
34pub struct Touchscreen {
35 connection: Arc<CdpConnection>,
37 session_id: String,
39 enabled: AtomicBool,
41}
42
43impl Touchscreen {
44 pub(crate) fn new(connection: Arc<CdpConnection>, session_id: String) -> Self {
46 Self {
47 connection,
48 session_id,
49 enabled: AtomicBool::new(false),
50 }
51 }
52
53 #[instrument(level = "debug", skip(self))]
72 pub async fn enable(&self) -> Result<(), LocatorError> {
73 self.enable_with_max_points(1).await
74 }
75
76 #[instrument(level = "debug", skip(self), fields(max_touch_points = max_touch_points))]
78 pub async fn enable_with_max_points(&self, max_touch_points: i32) -> Result<(), LocatorError> {
79 debug!("Enabling touch emulation with max_touch_points={}", max_touch_points);
80
81 self.connection
82 .send_command::<_, serde_json::Value>(
83 "Emulation.setTouchEmulationEnabled",
84 Some(SetTouchEmulationEnabledParams {
85 enabled: true,
86 max_touch_points: Some(max_touch_points),
87 }),
88 Some(&self.session_id),
89 )
90 .await?;
91
92 self.enabled.store(true, Ordering::SeqCst);
93 Ok(())
94 }
95
96 #[instrument(level = "debug", skip(self))]
98 pub async fn disable(&self) -> Result<(), LocatorError> {
99 debug!("Disabling touch emulation");
100
101 self.connection
102 .send_command::<_, serde_json::Value>(
103 "Emulation.setTouchEmulationEnabled",
104 Some(SetTouchEmulationEnabledParams {
105 enabled: false,
106 max_touch_points: None,
107 }),
108 Some(&self.session_id),
109 )
110 .await?;
111
112 self.enabled.store(false, Ordering::SeqCst);
113 Ok(())
114 }
115
116 pub fn is_enabled(&self) -> bool {
118 self.enabled.load(Ordering::SeqCst)
119 }
120
121 pub(crate) fn set_enabled(&self, enabled: bool) {
123 self.enabled.store(enabled, Ordering::SeqCst);
124 }
125
126 fn check_enabled(&self) -> Result<(), LocatorError> {
128 if !self.is_enabled() {
129 return Err(LocatorError::TouchNotEnabled);
130 }
131 Ok(())
132 }
133
134 #[instrument(level = "debug", skip(self), fields(x = x, y = y))]
155 pub async fn tap(&self, x: f64, y: f64) -> Result<(), LocatorError> {
156 self.check_enabled()?;
157 debug!("Tapping at ({}, {})", x, y);
158
159 let touch_id = TOUCH_ID_COUNTER.fetch_add(1, Ordering::SeqCst);
161
162 let mut touch_point = TouchPoint::new(x, y);
164 touch_point.id = Some(touch_id);
165
166 let start_params = DispatchTouchEventParams {
167 event_type: viewpoint_cdp::protocol::input::TouchEventType::TouchStart,
168 touch_points: vec![touch_point.clone()],
169 modifiers: None,
170 timestamp: None,
171 };
172
173 self.connection
174 .send_command::<_, serde_json::Value>(
175 "Input.dispatchTouchEvent",
176 Some(start_params),
177 Some(&self.session_id),
178 )
179 .await?;
180
181 let end_params = DispatchTouchEventParams {
183 event_type: viewpoint_cdp::protocol::input::TouchEventType::TouchEnd,
184 touch_points: vec![],
185 modifiers: None,
186 timestamp: None,
187 };
188
189 self.connection
190 .send_command::<_, serde_json::Value>(
191 "Input.dispatchTouchEvent",
192 Some(end_params),
193 Some(&self.session_id),
194 )
195 .await?;
196
197 Ok(())
198 }
199
200 #[instrument(level = "debug", skip(self), fields(x = x, y = y, modifiers = modifiers))]
206 pub async fn tap_with_modifiers(
207 &self,
208 x: f64,
209 y: f64,
210 modifiers: i32,
211 ) -> Result<(), LocatorError> {
212 self.check_enabled()?;
213 debug!("Tapping at ({}, {}) with modifiers {}", x, y, modifiers);
214
215 let touch_id = TOUCH_ID_COUNTER.fetch_add(1, Ordering::SeqCst);
216
217 let mut touch_point = TouchPoint::new(x, y);
218 touch_point.id = Some(touch_id);
219
220 let start_params = DispatchTouchEventParams {
222 event_type: viewpoint_cdp::protocol::input::TouchEventType::TouchStart,
223 touch_points: vec![touch_point],
224 modifiers: Some(modifiers),
225 timestamp: None,
226 };
227
228 self.connection
229 .send_command::<_, serde_json::Value>(
230 "Input.dispatchTouchEvent",
231 Some(start_params),
232 Some(&self.session_id),
233 )
234 .await?;
235
236 let end_params = DispatchTouchEventParams {
238 event_type: viewpoint_cdp::protocol::input::TouchEventType::TouchEnd,
239 touch_points: vec![],
240 modifiers: Some(modifiers),
241 timestamp: None,
242 };
243
244 self.connection
245 .send_command::<_, serde_json::Value>(
246 "Input.dispatchTouchEvent",
247 Some(end_params),
248 Some(&self.session_id),
249 )
250 .await?;
251
252 Ok(())
253 }
254}