use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
use cr1140_hal::sys::{read_backlight, read_led, set_backlight, set_kbd_backlight, Led, BACKLIGHT};
#[derive(Clone, Default)]
pub struct ShutdownFlag(Arc<AtomicBool>);
impl ShutdownFlag {
pub fn new() -> Self {
Self::default()
}
pub fn is_set(&self) -> bool {
self.0.load(Ordering::Relaxed)
}
#[cfg(feature = "signals")]
pub fn install_handler(&self) -> crate::SdkResult<()> {
signal_hook::flag::register(signal_hook::consts::SIGINT, Arc::clone(&self.0))?;
signal_hook::flag::register(signal_hook::consts::SIGTERM, Arc::clone(&self.0))?;
Ok(())
}
#[cfg(test)]
fn raise(&self) {
self.0.store(true, Ordering::Relaxed);
}
}
pub struct ShutdownGuard {
backlight_name: String,
backlight: u32,
kbd: (u8, u8, u8),
flag: ShutdownFlag,
}
impl ShutdownGuard {
pub fn capture() -> crate::SdkResult<Self> {
Self::capture_for(BACKLIGHT)
}
pub fn capture_for(backlight_name: &str) -> crate::SdkResult<Self> {
let backlight = read_backlight(backlight_name)?;
let r = read_led(Led::KbdRed.name())?.min(255) as u8;
let g = read_led(Led::KbdGreen.name())?.min(255) as u8;
let b = read_led(Led::KbdBlue.name())?.min(255) as u8;
Ok(Self {
backlight_name: backlight_name.to_string(),
backlight,
kbd: (r, g, b),
flag: ShutdownFlag::new(),
})
}
#[cfg(feature = "signals")]
pub fn install_signal_handler(&self) -> crate::SdkResult<()> {
self.flag.install_handler()
}
pub fn flag(&self) -> ShutdownFlag {
self.flag.clone()
}
pub fn should_shutdown(&self) -> bool {
self.flag.is_set()
}
#[cfg(test)]
fn inert_for_test() -> Self {
Self {
backlight_name: String::new(),
backlight: 0,
kbd: (0, 0, 0),
flag: ShutdownFlag::new(),
}
}
}
impl Drop for ShutdownGuard {
fn drop(&mut self) {
if self.backlight_name.is_empty() {
return;
}
if let Err(e) = set_backlight(&self.backlight_name, self.backlight) {
tracing::warn!(error = %e, "shutdown guard: backlight restore failed");
}
let (r, g, b) = self.kbd;
if let Err(e) = set_kbd_backlight(r, g, b) {
tracing::warn!(error = %e, "shutdown guard: kbd LED restore failed");
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn flag_starts_clear_and_reflects_raise() {
let flag = ShutdownFlag::new();
assert!(!flag.is_set());
flag.raise();
assert!(flag.is_set());
}
#[test]
fn cloned_flag_shares_state() {
let a = ShutdownFlag::new();
let b = a.clone();
a.raise();
assert!(b.is_set(), "clone must share the same AtomicBool");
}
#[test]
fn guard_should_shutdown_tracks_its_flag() {
let g = ShutdownGuard::inert_for_test();
assert!(!g.should_shutdown());
g.flag.raise();
assert!(g.should_shutdown());
}
}