use std::sync::Arc;
use rand::SeedableRng;
use tokio::sync::Mutex;
use zendriver_stealth::InputProfile;
use crate::input::pointer_state::MouseButtonSet;
pub mod bezier;
pub mod keyboard;
pub mod mouse;
pub mod pointer_state;
pub use keyboard::{Key, KeyModifiers, KeySequence, SpecialKey};
pub use mouse::MouseButton;
#[derive(Debug)]
pub struct InputController {
#[allow(dead_code)]
pub(crate) state: Mutex<InputState>,
#[allow(dead_code)]
pub(crate) profile: InputProfile,
}
#[allow(dead_code)]
#[derive(Debug)]
pub(crate) struct InputState {
pub pointer_x: f64,
pub pointer_y: f64,
pub buttons_held: MouseButtonSet,
pub modifiers_held: KeyModifiers,
pub rng: rand::rngs::SmallRng,
}
impl InputController {
#[must_use]
pub fn new(profile: InputProfile) -> Arc<Self> {
Arc::new(Self {
state: Mutex::new(InputState {
pointer_x: 0.0,
pointer_y: 0.0,
buttons_held: MouseButtonSet::empty(),
modifiers_held: KeyModifiers::empty(),
rng: rand::rngs::SmallRng::from_entropy(),
}),
profile,
})
}
#[cfg(any(test, feature = "testing"))]
#[must_use]
pub fn new_with_seed(profile: InputProfile, seed: u64) -> Arc<Self> {
Arc::new(Self {
state: Mutex::new(InputState {
pointer_x: 0.0,
pointer_y: 0.0,
buttons_held: MouseButtonSet::empty(),
modifiers_held: KeyModifiers::empty(),
rng: rand::rngs::SmallRng::seed_from_u64(seed),
}),
profile,
})
}
}
#[cfg(test)]
#[allow(clippy::panic, clippy::unwrap_used)]
mod tests {
use super::*;
#[tokio::test]
async fn new_initializes_zeroed_pointer_and_empty_buttons() {
let ic = InputController::new(InputProfile::native());
let s = ic.state.lock().await;
assert_eq!(s.pointer_x, 0.0);
assert_eq!(s.pointer_y, 0.0);
assert!(s.buttons_held.is_empty());
assert!(s.modifiers_held.is_empty());
}
#[tokio::test]
async fn new_with_seed_is_deterministic() {
let a = InputController::new_with_seed(InputProfile::native(), 42);
let b = InputController::new_with_seed(InputProfile::native(), 42);
let av = {
let mut s = a.state.lock().await;
rand::RngCore::next_u64(&mut s.rng)
};
let bv = {
let mut s = b.state.lock().await;
rand::RngCore::next_u64(&mut s.rng)
};
assert_eq!(av, bv);
}
#[test]
fn profile_is_stored_verbatim() {
let ic = InputController::new(InputProfile::spoofed());
assert!(ic.profile.typo_rate > 0.0);
let ic2 = InputController::new(InputProfile::native());
assert_eq!(ic2.profile.typo_rate, 0.0);
}
}