use futures::future::join_all; use once_cell::sync::OnceCell; use paris::{error, info}; use serde::{Deserialize, Serialize}; use std::{ collections::{HashMap, HashSet}, sync::Arc, time::Duration, }; use tauri::async_runtime::RwLock; use tokio::{ sync::mpsc, time::{sleep, Instant}, }; use tracing::warn; use crate::{ picker::{ self, config::DisplayConfig, display_picker::DisplayPicker, led_color::LedColor, screenshot::Screenshot, }, rpc, }; #[derive(Debug, Serialize, Deserialize, Clone, Copy)] pub enum AmbientLightMode { None, Follow, Flowing, } pub struct CoreManager { ambient_light_mode: Arc>, } impl CoreManager { pub fn global() -> &'static CoreManager { static CORE_MANAGER: OnceCell = OnceCell::new(); let core = CORE_MANAGER.get_or_init(|| CoreManager { ambient_light_mode: Arc::new(RwLock::new(AmbientLightMode::None)), }); core } pub async fn set_ambient_light(&self, target_mode: AmbientLightMode) { let mut mode = self.ambient_light_mode.write().await; *mode = target_mode; drop(mode); match target_mode { AmbientLightMode::Flowing => self.play_flowing_light().await, AmbientLightMode::None => {} AmbientLightMode::Follow => match self.play_follow().await { Ok(_) => {} Err(error) => { warn!("Can not following displays. {}", error); } }, }; } pub async fn play_flowing_light(&self) { let mut hue = 0f64; let step_length = 2.0; loop { let lock = self.ambient_light_mode.read().await; if let AmbientLightMode::Flowing = *lock { let mut colors = Vec::::new(); for i in 0..60 { let color = LedColor::from_hsv((hue + i as f64 * step_length) % 360.0, 1.0, 0.5); colors.push(color); } hue = (hue + 1.0) % 360.0; match rpc::manager::Manager::global() .publish_led_colors(&colors) .await { Ok(_) => {} Err(error) => { warn!("publish led colors failed. {}", error); } } } else { break; } sleep(Duration::from_millis(50)).await; } } pub async fn play_follow(&self) -> anyhow::Result<()> { let mut futs = vec![]; let configs = picker::config::Manager::global().reload_config().await; let configs = match configs { Ok(c) => c.display_configs, Err(err) => anyhow::bail!("can not get display configs. {:?}", err), }; info!("piker display configs: {:?}", configs); let (tx, mut rx) = mpsc::channel(10); for config in configs.clone() { let tx = tx.clone(); let fut = tokio::spawn(async move { match Self::follow_display_by_config(config, tx).await { Ok(_) => {} Err(error) => { warn!("following failed. {}", error); } } }); futs.push(fut); } let total_colors_count = configs .iter() .flat_map(|c| { vec![ c.led_strip_of_borders.top, c.led_strip_of_borders.bottom, c.led_strip_of_borders.left, c.led_strip_of_borders.right, ] }) .flat_map(|l| match l { Some(l) => (l.global_start_position.min(l.global_end_position) ..l.global_start_position.max(l.global_end_position)) .collect(), None => { vec![] } }) .collect::>() .len(); tokio::spawn(async move { let mut global_sub_pixels = HashMap::new(); while let Some(screenshot) = rx.recv().await { let start_at = Instant::now(); let colors = screenshot.get_colors(); let config = screenshot.get_config(); for (colors, config) in vec![ (colors.top, config.led_strip_of_borders.top), (colors.right, config.led_strip_of_borders.right), (colors.bottom, config.led_strip_of_borders.bottom), (colors.left, config.led_strip_of_borders.left), ] { match config { Some(config) => { let (sign, start) = if config.global_start_position <= config.global_end_position { (1, config.global_start_position as isize * 3) } else { (-1, (config.global_start_position as isize + 1) * 3 - 1) }; for (index, color) in colors.into_iter().enumerate() { let pixel_index = index / 3; let sub_pixel_index = index % 3; let offset = if sign < 0 { 2 - sub_pixel_index } else { sub_pixel_index }; let global_sub_pixel_index = (sign * (pixel_index as isize * 3 + offset as isize) + start ) as usize; global_sub_pixels.insert(global_sub_pixel_index, color); } } None => {} } } // info!( // "led count: {}, spend: {:?}", // global_sub_pixels.len(), // start_at.elapsed() // ); if global_sub_pixels.len() >= total_colors_count * 3 { let mut colors = vec![]; for index in 0..global_sub_pixels.len() { colors.push(*global_sub_pixels.get(&index).unwrap()); } // info!("{:?}", colors); global_sub_pixels = HashMap::new(); match rpc::manager::Manager::global() .publish_led_sub_pixels(colors) .await { Ok(_) => { // info!("publish successful",); } Err(error) => { warn!("publish led colors failed. {}", error); } } } } }); join_all(futs).await; Ok(()) } async fn follow_display_by_config( config: DisplayConfig, tx: mpsc::Sender, ) -> anyhow::Result<()> { let mut picker = DisplayPicker::from_config(config)?; info!("width: {}", picker.config.display_width); loop { let start = Instant::now(); let next_tick = start + Duration::from_millis(16); let lock = Self::global().ambient_light_mode.read().await; if let AmbientLightMode::Follow = *lock { drop(lock); let screenshot = picker.take_screenshot()?; info!("Take Screenshot Spend: {:?}", start.elapsed()); match tx.send(screenshot).await { Ok(_) => {} Err(err) => { error!("send screenshot to main thread was failed. {:?}", err); } }; } else { break; } tokio::time::sleep_until(next_tick).await; } Ok(()) } }