|
|
|
@ -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<RwLock<Vec<Arc<RwLock<DisplayHandler>>>>>,
|
|
|
|
|
setting_request_handler: Option<tokio::task::JoinHandle<()>>,
|
|
|
|
|
displays_changed_sender: Arc<watch::Sender<Vec<DisplayState>>>,
|
|
|
|
|
auto_save_state_handler: Option<tokio::task::JoinHandle<()>>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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<DisplayState> {
|
|
|
|
|
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::<DisplayStateWrapper>(&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<RwLock<Vec<Arc<RwLock<DisplayHandler>>>>>) {
|
|
|
|
|
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<Vec<DisplayState>> {
|
|
|
|
|
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();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|