hinoirisetr 1.6.3

A daemon to dim the screen at night
Documentation
use std::process::Command;
use std::sync::atomic::{AtomicU16, Ordering};
use std::sync::{Arc, LazyLock, Mutex};

#[cfg(feature = "wayland-backend")]
use crate::backend::WAYLAND_THREAD;
use crate::utils::eq_ignore_case;
use crate::{debug, ensure_hyprsunset_running, error, trace, warn};

static TEMP_LOCK: LazyLock<Arc<Mutex<()>>> = LazyLock::new(|| Arc::new(Mutex::new(())));
// cache the last temperature so that we don't apply it again
static LAST_TEMP: AtomicU16 = AtomicU16::new(0);

#[derive(Debug, PartialEq, Eq, Copy, Clone)]
pub enum TempBackend {
    Hyprctl,
    Xsct,
    #[cfg(feature = "wayland-backend")]
    Wayland,
    None,
}

/// it is case insensitive
impl std::str::FromStr for TempBackend {
    type Err = String;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s {
            s if eq_ignore_case(s, "hyprctl") => {
                warn!(
                    "hyprctl backend has been superseded by wayland, it may be removed in the next release"
                );
                Ok(Self::Hyprctl)
            },
            s if eq_ignore_case(s, "gammastep") => Err(
                "gammastep backend has been deprecated, use wayland or xset instead".to_string(),
            ),
            s if eq_ignore_case(s, "xsct") => Ok(Self::Xsct),
            s if eq_ignore_case(s, "redshift") => {
                Err("redshift backend has been deprecated, use wayland or xset instead".to_string())
            },
            #[cfg(feature = "wayland-backend")]
            s if eq_ignore_case(s, "wayland") => Ok(Self::Wayland),
            #[cfg(not(feature = "wayland-backend"))]
            s if eq_ignore_case(s, "wayland") => {
                Err("hinoirisetr was compiled without wayland support".to_string())
            },
            s if eq_ignore_case(s, "none") => Ok(Self::None),
            _ => Err(format!("Invalid GammaBackend: {s}")),
        }
    }
}

/// Apply given temperature (Kelvin) via given backends
pub fn apply_temp(temp: u16, backends: &[TempBackend]) {
    trace!("apply_temp({temp})");
    let _lock = TEMP_LOCK.lock().unwrap();
    let last_temp = LAST_TEMP.load(Ordering::SeqCst);
    if last_temp == temp {
        trace!("Settings unchanged, skipping application");
        return;
    }
    debug!("applying temperature: {temp}");

    if temp != last_temp {
        for backend in backends {
            match backend {
                TempBackend::Hyprctl => match ensure_hyprsunset_running() {
                    Ok(()) => {
                        let _ = Command::new("hyprctl")
                            .args(["hyprsunset", "temperature", &temp.to_string()])
                            .output();
                        trace!("hyprctl hyprsunset temperature {temp}");
                    },
                    Err(err) => {
                        error!("Error while starting hyprsunset: {err}");
                    },
                },
                TempBackend::Xsct => {
                    let _ = Command::new("xsct").args([&temp.to_string()]).output();
                    trace!("xsct {temp}");
                },
                TempBackend::None => {},
                #[cfg(feature = "wayland-backend")]
                TempBackend::Wayland => {
                    let guard = WAYLAND_THREAD.read().unwrap();
                    if let Some(ref thread) = *guard {
                        thread.set_temperature(temp.into());
                    }
                },
            }
        }
        LAST_TEMP.store(temp, Ordering::SeqCst);
    }
}

pub fn reset_cache() {
    LAST_TEMP.store(0, Ordering::SeqCst);
}