meth/
lib.rs

1#![doc = include_str!("../README.md")]
2
3use std::{thread::{spawn, sleep}, time::Duration, sync::mpsc::Receiver};
4use mouse_rs::{Mouse, types::Point};
5mod wiggler;
6
7/// Wiggles immediately. If mouse does not move for `30s`, wiggle again, keeping the desktop awake.
8/// 
9/// ### Things to know:
10/// 
11/// - __Sleep prevention stops when `Meth` is dropped.__
12/// - Manages its own background thread, idle sleeping most of the time.
13/// - Background thread may persist for up to `30s` after `Meth` is dropped.
14/// 
15/// ```rust
16/// // computer will not sleep until
17/// // `important_computation()` completes.
18/// {
19///   let meth = Meth::new();
20///   important_computation();
21/// }
22/// 
23/// // `meth` dropped,
24/// // computer might enter sleep now.
25/// ```
26pub struct Meth {
27  /// tx handle to kill background thread (which occasionally wiggles the mouse)
28  terminate: std::sync::mpsc::Sender<()>
29}
30
31impl Drop for Meth {
32  fn drop(&mut self) {
33    // can only fail when receiver is dropped first
34    // (in which case our goal is already achieved)
35    let _ = self.terminate.send(());
36  }
37}
38
39impl Meth {
40  /// Wait 30s between wiggle attempts
41  const SLEEP_PERIOD: Duration = Duration::from_secs(30);
42
43  /// Create a new Meth instance
44  pub fn new() -> Self {
45    let (terminate, rx) = std::sync::mpsc::channel();
46    
47    spawn(move || {
48      loop {
49        // check for termination signal
50        if let Ok(_) = rx.try_recv() { break }
51        
52        // if some error occurs, continue to revive thread
53        if Self::keep_awake(&rx).is_ok() { break }
54        sleep(Self::SLEEP_PERIOD)
55      }
56    });
57
58    Meth { terminate }
59  }
60
61  /// Are these two points identical?
62  fn identical(a: &Point, b: &Point) -> bool {
63    a.x == b.x && 
64    a.y == b.y
65  }
66
67  /// Fallible inner control loop.
68  /// - Will wiggle immediately, in case we are about to sleep.
69  fn keep_awake(rx: &Receiver<()>) -> Result<(), Box<dyn std::error::Error>> {
70    let mouse = Mouse::new();
71    let mut position = mouse.get_position().unwrap();
72    let mut wiggler = wiggler::Wiggler::default();
73
74    loop {
75      // check for termination signal
76      if let Ok(_) = rx.try_recv() { break }
77      
78      {
79        let mut p = mouse.get_position()?;
80      
81        // if the mouse has not moved since last poll
82        if Self::identical(&p, &position) {
83          // wiggle it
84          wiggler.transform(&mut p);
85          mouse.move_to(p.x as i32, p.y as i32)?;
86        }
87
88        position = p;
89      };
90
91      // return to sleep
92      sleep(Meth::SLEEP_PERIOD);
93    }
94
95    Ok(())
96  }
97}
98
99
100#[cfg(test)]
101mod test {
102  use std::time::Duration;
103  use crate::Meth;
104
105  #[test]
106  fn test_move() {
107    {
108      println!("Wiggling soon!");
109      let _meth = Meth::new();
110      std::thread::sleep(Duration::from_secs(60));
111    }
112
113    println!("No more wiggles!");
114    std::thread::sleep(Duration::from_secs(90));
115  }
116
117  #[test]
118  fn two() {
119    {
120      println!("Wiggling soon!");
121      let _meth_a = Meth::new();
122      let _meth_b = Meth::new();
123      std::thread::sleep(Duration::from_secs(60));
124    }
125
126    println!("No more wiggles!");
127    std::thread::sleep(Duration::from_secs(90));
128  }
129}