use parking_lot::{Condvar, Mutex};
struct State {
background: bool,
playing: bool,
holds: u32,
applied: Applied,
}
#[derive(Clone, Copy, PartialEq, Eq)]
struct Applied {
awake: bool,
findable: bool,
}
static STATE: Mutex<State> = Mutex::new(State {
background: false,
playing: false,
holds: 0,
applied: Applied {
awake: true,
findable: true,
},
});
static CHANGED: Condvar = Condvar::new();
static APPLYING: Mutex<()> = Mutex::new(());
fn is_awake(s: &State) -> bool {
!s.background || s.holds > 0 || crate::remote::devices::target().is_some()
}
fn is_findable(s: &State) -> bool {
is_awake(s) || s.playing
}
pub fn awake() -> bool {
is_awake(&STATE.lock())
}
pub fn findable() -> bool {
is_findable(&STATE.lock())
}
pub fn wait_until_awake() {
let mut s = STATE.lock();
while !is_awake(&s) {
CHANGED.wait(&mut s);
}
}
pub fn set_background(background: bool) {
update(|s| s.background = background);
}
pub fn set_playing(playing: bool) {
update(|s| s.playing = playing);
}
pub fn hold() {
update(|s| s.holds += 1);
}
pub fn release() {
update(|s| s.holds = s.holds.saturating_sub(1));
}
pub fn reapply() {
update(|_| ());
}
fn update(f: impl FnOnce(&mut State)) {
let _applying = APPLYING.lock();
let (before, now) = {
let mut s = STATE.lock();
f(&mut s);
let now = Applied {
awake: is_awake(&s),
findable: is_findable(&s),
};
CHANGED.notify_all();
(std::mem::replace(&mut s.applied, now), now)
};
if before.findable != now.findable {
crate::remote::nearby::reconfigure();
}
if before.awake != now.awake {
if now.awake {
log::info!("quiet: awake");
crate::remote::nearby::wake();
crate::remote::devices::resume();
} else {
log::info!("quiet: nothing runs until asked");
crate::remote::link::hang_up();
crate::remote::nearby::suspend();
}
}
}
#[cfg(test)]
mod tests {
use super::*;
fn state(background: bool, playing: bool, holds: u32) -> State {
State {
background,
playing,
holds,
applied: Applied {
awake: true,
findable: true,
},
}
}
#[test]
fn in_front_everything_runs() {
let s = state(false, false, 0);
assert!(is_awake(&s) && is_findable(&s));
}
#[test]
fn playing_in_the_background_is_found_but_runs_nothing() {
let s = state(true, true, 0);
assert!(!is_awake(&s));
assert!(is_findable(&s));
}
#[test]
fn paused_in_the_background_is_not_there() {
let s = state(true, false, 0);
assert!(!is_awake(&s) && !is_findable(&s));
}
#[test]
fn a_push_holds_it_awake() {
let s = state(true, false, 1);
assert!(is_awake(&s) && is_findable(&s));
}
}