From 3a23e1760bc4def9e2a55ff902977029fd67b51c Mon Sep 17 00:00:00 2001 From: Ivan Li Date: Thu, 11 May 2023 14:13:14 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=94=AF=E6=8C=81=E8=AE=B0=E4=BD=8F?= =?UTF-8?q?=E6=98=BE=E7=A4=BA=E5=99=A8=E9=85=8D=E7=BD=AE=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src-tauri/src/display/display_handler.rs | 2 +- src-tauri/src/display/display_state.rs | 12 +++ src-tauri/src/display/manager.rs | 129 ++++++++++++++++++++++- 3 files changed, 139 insertions(+), 4 deletions(-) diff --git a/src-tauri/src/display/display_handler.rs b/src-tauri/src/display/display_handler.rs index ac3903b..31c28ec 100644 --- a/src-tauri/src/display/display_handler.rs +++ b/src-tauri/src/display/display_handler.rs @@ -14,7 +14,7 @@ impl DisplayHandler { pub async fn fetch_state(&self) { let mut controller = self.controller.write().await; - let mut temp_state = DisplayState::default(); + let mut temp_state = self.state.read().await.clone(); match controller.handle.get_vcp_feature(0x10) { Ok(value) => { diff --git a/src-tauri/src/display/display_state.rs b/src-tauri/src/display/display_state.rs index d70fc18..dc9a7e7 100644 --- a/src-tauri/src/display/display_state.rs +++ b/src-tauri/src/display/display_state.rs @@ -34,3 +34,15 @@ impl DisplayState { } } } + +#[derive(Clone, Serialize, Deserialize, Debug)] +pub struct DisplayStateWrapper { + pub version: u8, + pub states: Vec, +} + +impl DisplayStateWrapper { + pub fn new(states: Vec) -> Self { + Self { version: 1, states } + } +} diff --git a/src-tauri/src/display/manager.rs b/src-tauri/src/display/manager.rs index 78a4916..f0cc922 100644 --- a/src-tauri/src/display/manager.rs +++ b/src-tauri/src/display/manager.rs @@ -1,23 +1,31 @@ use std::{ + env::current_dir, sync::Arc, time::{Duration, SystemTime}, }; use ddc_hi::Display; use paris::{error, info, warn}; +use tauri::api::path::config_dir; use tokio::{ sync::{broadcast, watch, OnceCell, RwLock}, task::yield_now, }; -use crate::rpc::{BoardMessageChannels, DisplaySetting}; +use crate::{ + display::DisplayStateWrapper, + rpc::{BoardMessageChannels, DisplaySetting}, +}; use super::{display_handler::DisplayHandler, display_state::DisplayState}; +const CONFIG_FILE_NAME: &str = "cc.ivanli.ambient_light/displays.toml"; + pub struct DisplayManager { displays: Arc>>>>, setting_request_handler: Option>, displays_changed_sender: Arc>>, + auto_save_state_handler: Option>, } impl DisplayManager { @@ -35,12 +43,29 @@ impl DisplayManager { displays: Arc::new(RwLock::new(Vec::new())), setting_request_handler: None, displays_changed_sender, + auto_save_state_handler: None, }; instance.fetch_displays().await; + instance.restore_states().await; + instance.fetch_state_of_displays().await; instance.subscribe_setting_request(); + instance.auto_save_state_of_displays(); instance } + fn auto_save_state_of_displays(&mut self) { + let displays = self.displays.clone(); + + let handler = tokio::spawn(async move { + loop { + tokio::time::sleep(Duration::from_secs(10)).await; + Self::save_states(displays.clone()).await; + } + }); + + self.auto_save_state_handler = Some(handler); + } + async fn fetch_displays(&self) { let mut displays = self.displays.write().await; displays.clear(); @@ -55,12 +80,19 @@ impl DisplayManager { controller: controller.clone(), }; - handler.fetch_state().await; - displays.push(Arc::new(RwLock::new(handler))); } } + async fn fetch_state_of_displays(&self) { + let displays = self.displays.read().await; + + for display in displays.iter() { + let display = display.read().await; + display.fetch_state().await; + } + } + pub async fn get_displays(&self) -> Vec { let displays = self.displays.read().await; let mut states = Vec::new(); @@ -132,6 +164,93 @@ impl DisplayManager { self.setting_request_handler = Some(handler); } + async fn restore_states(&self) { + let path = config_dir() + .unwrap_or(current_dir().unwrap()) + .join(CONFIG_FILE_NAME); + + if !path.exists() { + log::info!("config file not found: {}. skip read.", path.display()); + return; + } + + let text = std::fs::read_to_string(path); + if let Err(err) = text { + log::error!("failed to read config file: {}", err); + return; + } + + let text = text.unwrap(); + let wrapper = toml::from_str::(&text); + + if let Err(err) = wrapper { + log::error!("failed to parse display states file: {}", err); + return; + } + + let states = wrapper.unwrap().states; + + let displays = self.displays.read().await; + for (index, display) in displays.iter().enumerate() { + let display = display.read().await; + let mut state = display.state.write().await; + let saved = states.get(index); + if let Some(saved) = saved { + state.brightness = saved.brightness; + state.contrast = saved.contrast; + state.mode = saved.mode; + log::info!("restore display config. display#{}: {:?}", index, state); + } + } + + log::info!( + "restore display config. store displays: {}, online displays: {}", + states.len(), + displays.len() + ); + } + + async fn save_states(displays: Arc>>>>) { + let path = config_dir() + .unwrap_or(current_dir().unwrap()) + .join(CONFIG_FILE_NAME); + + let displays = displays.read().await; + let mut states = Vec::new(); + for display in displays.iter() { + let state = display.read().await.state.read().await.clone(); + states.push(state); + } + + let wrapper = DisplayStateWrapper::new(states); + + let text = toml::to_string(&wrapper); + if let Err(err) = text { + log::error!("failed to serialize display states: {}", err); + log::error!("display states: {:?}", &wrapper); + return; + } + + let text = text.unwrap(); + if path.exists() { + if let Err(err) = std::fs::remove_file(&path) { + log::error!("failed to remove old config file: {}", err); + return; + } + } + + if let Err(err) = std::fs::write(&path, text) { + log::error!("failed to write config file: {}", err); + return; + } + + log::info!( + "save display config. store displays: {}, online displays: {}", + wrapper.states.len(), + displays.len() + ); + } + pub fn subscribe_displays_changed(&self) -> watch::Receiver> { self.displays_changed_sender.subscribe() } @@ -143,5 +262,9 @@ impl Drop for DisplayManager { if let Some(handler) = self.setting_request_handler.take() { handler.abort(); } + + if let Some(handler) = self.auto_save_state_handler.take() { + handler.abort(); + } } }