1use std::time::{Duration, SystemTime, UNIX_EPOCH};
2
3#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
5#[non_exhaustive]
6pub enum ClickTiming {
7 #[default]
9 Makcu,
10 Ferrum,
12 Fixed(Duration),
13 Off,
15}
16
17impl ClickTiming {
18 fn range(self) -> Option<(Duration, Duration)> {
19 match self {
20 Self::Makcu => Some((Duration::from_millis(35), Duration::from_millis(75))),
21 Self::Ferrum => Some((Duration::from_millis(75), Duration::from_millis(125))),
22 Self::Fixed(hold) => Some((hold, hold)),
23 Self::Off => None,
24 }
25 }
26}
27
28#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
29#[non_exhaustive]
30pub enum Timing {
31 #[default]
32 Any,
33 NativeOnly,
35}
36
37#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
38#[non_exhaustive]
39pub struct Click {
40 pub hold: Option<Duration>,
42 pub timing: Timing,
43}
44
45impl Click {
46 pub fn hold(hold: Duration) -> Self {
47 Self {
48 hold: Some(hold),
49 timing: Timing::Any,
50 }
51 }
52
53 pub fn native_only(self) -> Self {
54 Self {
55 timing: Timing::NativeOnly,
56 ..self
57 }
58 }
59}
60
61#[derive(Debug)]
63pub(crate) struct Holds(u64);
64
65impl Holds {
66 pub(crate) fn seeded(seed: u64) -> Self {
67 Self(seed | 1)
69 }
70
71 pub(crate) fn from_clock() -> Self {
72 let nanos = SystemTime::now()
73 .duration_since(UNIX_EPOCH)
74 .map_or(0, |since| since.as_nanos() as u64);
75 Self::seeded(nanos)
76 }
77
78 pub(crate) fn pick(&mut self, timing: ClickTiming) -> Option<Duration> {
79 let (min, max) = timing.range()?;
80 let span = (max - min).as_micros() as u64;
81 if span == 0 {
82 return Some(min);
83 }
84 self.0 ^= self.0 << 13;
85 self.0 ^= self.0 >> 7;
86 self.0 ^= self.0 << 17;
87 Some(min + Duration::from_micros(self.0 % (span + 1)))
88 }
89}
90
91#[cfg(test)]
92mod tests;