1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
//! Keep your computer awake.
//!
//! # Examples
//!
//! ```
//! # fn try_main() -> anyhow::Result<()> {
//! let _awake = keep_active::Builder::default()
//!     .display(true)
//!     .reason("Video playback")
//!     .app_name("My prog")
//!     .app_reverse_domain("io.github.myprog")
//!     .create()?;
//! # Ok(())
//! # }
//! # try_main();
//! ```
//!
//! ```
//! # fn try_main() -> anyhow::Result<()> {
//! let _awake = keep_active::Builder::default()
//!     .display(true)
//!     .idle(true)
//!     .sleep(true)
//!     .create()?;
//! # Ok(())
//! # }
//! # try_main();
//! ```

use anyhow::Result;
use derive_builder::Builder;
use enigo::{Enigo, Key, KeyboardControllable, MouseControllable};
use std::{thread, time::Duration};

mod sys;

#[derive(Builder, Debug)]
#[builder(public, name = "Builder", build_fn(private))]
#[allow(dead_code)] // Some fields are unused on some platforms
struct Options {
    /// Prevent the display from turning off.
    #[builder(default)]
    display: bool,

    /// Prevent the system from sleeping due to idleness.
    #[builder(default)]
    idle: bool,

    /// Prevent the system from sleeping. Only works under certain, OS dependant, conditions.
    #[builder(default)]
    sleep: bool,

    // TODO Reconsider this defaults. They are really meant for the CLI.
    /// Reason the consumer is keeping the system awake. Defaults to `"User requested"`. (Used on Linux & macOS)
    #[builder(setter(into), default = "\"User requested\".to_string()")]
    reason: String,

    /// Name of the program keeping the system awake. Defaults to `"keep-active"`. (Used on Linux)
    #[builder(setter(into), default = "\"keep-active\".to_string()")]
    app_name: String,

    /// Reverse domain name of the program keeping the system awake. Defaults to `"io.github.segevfiner.keep-active"`. (Used on Linux)
    #[builder(
        setter(into),
        default = "\"io.github.segevfiner.keep-active\".to_string()"
    )]
    app_reverse_domain: String,
}

impl Builder {
    pub fn create(&self) -> Result<KeepActive> {
        Ok(KeepActive {
            _imp: sys::KeepActive::new(self.build()?)?,
        })
    }
}

/// Keeps the machine or display awake (as configured), until dropped. Create using [struct@Builder].
pub struct KeepActive {
    _imp: sys::KeepActive,
}

// TODO: exit gracefully on Ctrl+C 
pub fn simulate_activity() -> Result<(), Box<dyn std::error::Error>> {
    let mut enigo = Enigo::new();

    loop {
        // Move right
        for _ in 0..10 {
            enigo.mouse_move_relative(1, 0);
            thread::sleep(Duration::from_millis(100));
        }
        // Move down
        for _ in 0..10 {
            enigo.mouse_move_relative(0, 1);
            thread::sleep(Duration::from_millis(100));
        }
        // Move left
        for _ in 0..10 {
            enigo.mouse_move_relative(-1, 0);
            thread::sleep(Duration::from_millis(100));
        }
        // Move up
        for _ in 0..10 {
            enigo.mouse_move_relative(0, -1);
            thread::sleep(Duration::from_millis(100));
        }

        // Simulate a key press to keep activity
        enigo.key_down(Key::Shift);
        enigo.key_up(Key::Shift);

        thread::sleep(Duration::from_secs(60)); // TODO: Make this configurable
    }
}