use crate::platform::detect_system_color_scheme;
use crate::state::ThemeState;
use crate::theme::ColorScheme;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
use std::thread::{self, JoinHandle};
use std::time::Duration;
pub const DEFAULT_POLL_INTERVAL: Duration = Duration::from_secs(1);
pub struct SystemSchemeWatcher {
stop_signal: Arc<AtomicBool>,
thread_handle: Option<JoinHandle<()>>,
}
impl SystemSchemeWatcher {
pub fn start() -> Self {
Self::start_with_interval(DEFAULT_POLL_INTERVAL)
}
pub fn start_with_interval(interval: Duration) -> Self {
let stop_signal = Arc::new(AtomicBool::new(false));
let stop_signal_clone = Arc::clone(&stop_signal);
let thread_handle = thread::Builder::new()
.name("blinc-scheme-watcher".to_string())
.spawn(move || {
Self::watch_loop(stop_signal_clone, interval);
})
.expect("Failed to spawn scheme watcher thread");
Self {
stop_signal,
thread_handle: Some(thread_handle),
}
}
pub fn stop(&mut self) {
self.stop_signal.store(true, Ordering::SeqCst);
if let Some(handle) = self.thread_handle.take() {
let _ = handle.join();
}
}
pub fn is_running(&self) -> bool {
!self.stop_signal.load(Ordering::SeqCst)
&& self
.thread_handle
.as_ref()
.map(|h| !h.is_finished())
.unwrap_or(false)
}
fn watch_loop(stop_signal: Arc<AtomicBool>, interval: Duration) {
let mut last_scheme: Option<ColorScheme> = None;
tracing::debug!("System scheme watcher started (interval: {:?})", interval);
while !stop_signal.load(Ordering::SeqCst) {
let current_scheme = detect_system_color_scheme();
if last_scheme != Some(current_scheme) {
if let Some(previous) = last_scheme {
tracing::info!(
"System color scheme changed: {:?} -> {:?}",
previous,
current_scheme
);
if let Some(theme) = ThemeState::try_get() {
theme.set_scheme(current_scheme);
}
} else {
tracing::debug!("Initial system color scheme detected: {:?}", current_scheme);
}
last_scheme = Some(current_scheme);
}
thread::sleep(interval);
}
tracing::debug!("System scheme watcher stopped");
}
}
impl Drop for SystemSchemeWatcher {
fn drop(&mut self) {
self.stop();
}
}
#[derive(Clone, Debug)]
pub struct WatcherConfig {
pub poll_interval: Duration,
pub auto_start: bool,
}
impl Default for WatcherConfig {
fn default() -> Self {
Self {
poll_interval: DEFAULT_POLL_INTERVAL,
auto_start: true,
}
}
}
impl WatcherConfig {
pub fn new() -> Self {
Self::default()
}
pub fn poll_interval(mut self, interval: Duration) -> Self {
self.poll_interval = interval;
self
}
pub fn auto_start(mut self, enabled: bool) -> Self {
self.auto_start = enabled;
self
}
pub fn build(self) -> Option<SystemSchemeWatcher> {
if self.auto_start {
Some(SystemSchemeWatcher::start_with_interval(self.poll_interval))
} else {
None
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_watcher_config_default() {
let config = WatcherConfig::default();
assert_eq!(config.poll_interval, DEFAULT_POLL_INTERVAL);
assert!(config.auto_start);
}
#[test]
fn test_watcher_config_builder() {
let config = WatcherConfig::new()
.poll_interval(Duration::from_secs(5))
.auto_start(false);
assert_eq!(config.poll_interval, Duration::from_secs(5));
assert!(!config.auto_start);
}
}