use std::sync::Arc;
use std::sync::atomic::{AtomicBool, AtomicI32, Ordering};
use tracing::{debug, instrument};
use viewpoint_cdp::CdpConnection;
use viewpoint_cdp::protocol::emulation::SetTouchEmulationEnabledParams;
use viewpoint_cdp::protocol::input::{DispatchTouchEventParams, TouchPoint};
use crate::error::LocatorError;
static TOUCH_ID_COUNTER: AtomicI32 = AtomicI32::new(0);
#[derive(Debug)]
pub struct Touchscreen {
connection: Arc<CdpConnection>,
session_id: String,
enabled: AtomicBool,
}
impl Touchscreen {
pub(crate) fn new(connection: Arc<CdpConnection>, session_id: String) -> Self {
Self {
connection,
session_id,
enabled: AtomicBool::new(false),
}
}
#[instrument(level = "debug", skip(self))]
pub async fn enable(&self) -> Result<(), LocatorError> {
self.enable_with_max_points(1).await
}
#[instrument(level = "debug", skip(self), fields(max_touch_points = max_touch_points))]
pub async fn enable_with_max_points(&self, max_touch_points: i32) -> Result<(), LocatorError> {
debug!(
"Enabling touch emulation with max_touch_points={}",
max_touch_points
);
self.connection
.send_command::<_, serde_json::Value>(
"Emulation.setTouchEmulationEnabled",
Some(SetTouchEmulationEnabledParams {
enabled: true,
max_touch_points: Some(max_touch_points),
}),
Some(&self.session_id),
)
.await?;
self.enabled.store(true, Ordering::SeqCst);
Ok(())
}
#[instrument(level = "debug", skip(self))]
pub async fn disable(&self) -> Result<(), LocatorError> {
debug!("Disabling touch emulation");
self.connection
.send_command::<_, serde_json::Value>(
"Emulation.setTouchEmulationEnabled",
Some(SetTouchEmulationEnabledParams {
enabled: false,
max_touch_points: None,
}),
Some(&self.session_id),
)
.await?;
self.enabled.store(false, Ordering::SeqCst);
Ok(())
}
pub fn is_enabled(&self) -> bool {
self.enabled.load(Ordering::SeqCst)
}
pub(crate) fn set_enabled(&self, enabled: bool) {
self.enabled.store(enabled, Ordering::SeqCst);
}
fn check_enabled(&self) -> Result<(), LocatorError> {
if !self.is_enabled() {
return Err(LocatorError::TouchNotEnabled);
}
Ok(())
}
#[instrument(level = "debug", skip(self), fields(x = x, y = y))]
pub async fn tap(&self, x: f64, y: f64) -> Result<(), LocatorError> {
self.check_enabled()?;
debug!("Tapping at ({}, {})", x, y);
let touch_id = TOUCH_ID_COUNTER.fetch_add(1, Ordering::SeqCst);
let mut touch_point = TouchPoint::new(x, y);
touch_point.id = Some(touch_id);
let start_params = DispatchTouchEventParams {
event_type: viewpoint_cdp::protocol::input::TouchEventType::TouchStart,
touch_points: vec![touch_point.clone()],
modifiers: None,
timestamp: None,
};
self.connection
.send_command::<_, serde_json::Value>(
"Input.dispatchTouchEvent",
Some(start_params),
Some(&self.session_id),
)
.await?;
let end_params = DispatchTouchEventParams {
event_type: viewpoint_cdp::protocol::input::TouchEventType::TouchEnd,
touch_points: vec![],
modifiers: None,
timestamp: None,
};
self.connection
.send_command::<_, serde_json::Value>(
"Input.dispatchTouchEvent",
Some(end_params),
Some(&self.session_id),
)
.await?;
Ok(())
}
#[instrument(level = "debug", skip(self), fields(x = x, y = y, modifiers = modifiers))]
pub async fn tap_with_modifiers(
&self,
x: f64,
y: f64,
modifiers: i32,
) -> Result<(), LocatorError> {
self.check_enabled()?;
debug!("Tapping at ({}, {}) with modifiers {}", x, y, modifiers);
let touch_id = TOUCH_ID_COUNTER.fetch_add(1, Ordering::SeqCst);
let mut touch_point = TouchPoint::new(x, y);
touch_point.id = Some(touch_id);
let start_params = DispatchTouchEventParams {
event_type: viewpoint_cdp::protocol::input::TouchEventType::TouchStart,
touch_points: vec![touch_point],
modifiers: Some(modifiers),
timestamp: None,
};
self.connection
.send_command::<_, serde_json::Value>(
"Input.dispatchTouchEvent",
Some(start_params),
Some(&self.session_id),
)
.await?;
let end_params = DispatchTouchEventParams {
event_type: viewpoint_cdp::protocol::input::TouchEventType::TouchEnd,
touch_points: vec![],
modifiers: Some(modifiers),
timestamp: None,
};
self.connection
.send_command::<_, serde_json::Value>(
"Input.dispatchTouchEvent",
Some(end_params),
Some(&self.session_id),
)
.await?;
Ok(())
}
}