1use std::sync::Arc;
14
15use rand::SeedableRng;
16use tokio::sync::Mutex;
17use zendriver_stealth::InputProfile;
18
19use crate::input::pointer_state::MouseButtonSet;
20
21pub mod bezier;
22pub mod keyboard;
23pub mod mouse;
24pub mod pointer_state;
25
26pub use keyboard::{Key, KeyModifiers, KeySequence, SpecialKey};
27pub use mouse::MouseButton;
28
29#[derive(Debug)]
40pub struct InputController {
41 #[allow(dead_code)]
44 pub(crate) state: Mutex<InputState>,
45 #[allow(dead_code)]
46 pub(crate) profile: InputProfile,
47}
48
49#[allow(dead_code)]
50#[derive(Debug)]
51pub(crate) struct InputState {
52 pub pointer_x: f64,
53 pub pointer_y: f64,
54 pub buttons_held: MouseButtonSet,
55 pub modifiers_held: KeyModifiers,
56 pub rng: rand::rngs::SmallRng,
57}
58
59impl InputController {
60 #[must_use]
75 pub fn new(profile: InputProfile) -> Arc<Self> {
76 Arc::new(Self {
77 state: Mutex::new(InputState {
78 pointer_x: 0.0,
79 pointer_y: 0.0,
80 buttons_held: MouseButtonSet::empty(),
81 modifiers_held: KeyModifiers::empty(),
82 rng: rand::rngs::SmallRng::from_entropy(),
83 }),
84 profile,
85 })
86 }
87
88 #[cfg(any(test, feature = "testing"))]
91 #[must_use]
92 pub fn new_with_seed(profile: InputProfile, seed: u64) -> Arc<Self> {
93 Arc::new(Self {
94 state: Mutex::new(InputState {
95 pointer_x: 0.0,
96 pointer_y: 0.0,
97 buttons_held: MouseButtonSet::empty(),
98 modifiers_held: KeyModifiers::empty(),
99 rng: rand::rngs::SmallRng::seed_from_u64(seed),
100 }),
101 profile,
102 })
103 }
104}
105
106#[cfg(test)]
107#[allow(clippy::panic, clippy::unwrap_used)]
108mod tests {
109 use super::*;
110
111 #[tokio::test]
112 async fn new_initializes_zeroed_pointer_and_empty_buttons() {
113 let ic = InputController::new(InputProfile::native());
114 let s = ic.state.lock().await;
115 assert_eq!(s.pointer_x, 0.0);
116 assert_eq!(s.pointer_y, 0.0);
117 assert!(s.buttons_held.is_empty());
118 assert!(s.modifiers_held.is_empty());
119 }
120
121 #[tokio::test]
122 async fn new_with_seed_is_deterministic() {
123 let a = InputController::new_with_seed(InputProfile::native(), 42);
127 let b = InputController::new_with_seed(InputProfile::native(), 42);
128 let av = {
129 let mut s = a.state.lock().await;
130 rand::RngCore::next_u64(&mut s.rng)
131 };
132 let bv = {
133 let mut s = b.state.lock().await;
134 rand::RngCore::next_u64(&mut s.rng)
135 };
136 assert_eq!(av, bv);
137 }
138
139 #[test]
140 fn profile_is_stored_verbatim() {
141 let ic = InputController::new(InputProfile::spoofed());
142 assert!(ic.profile.typo_rate > 0.0);
143 let ic2 = InputController::new(InputProfile::native());
144 assert_eq!(ic2.profile.typo_rate, 0.0);
145 }
146}