diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index 1473c3f..54a23b7 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -856,6 +856,7 @@ dependencies = [ "mdns", "once_cell", "paris", + "redb", "rumqttc", "scrap", "serde", @@ -2732,6 +2733,16 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "pyo3-build-config" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75439f995d07ddfad42b192dfcf3bc66a7ecfd8b4a1f5f6f046aa5c2c5d7677d" +dependencies = [ + "once_cell", + "target-lexicon", +] + [[package]] name = "quick-error" version = "1.2.3" @@ -2868,6 +2879,16 @@ dependencies = [ "num_cpus", ] +[[package]] +name = "redb" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78f210bb101d3a0ddba42f67b12a1d7186e584733ad028f119c8d217d867f03d" +dependencies = [ + "libc", + "pyo3-build-config", +] + [[package]] name = "redox_syscall" version = "0.2.16" @@ -3614,6 +3635,12 @@ dependencies = [ "xattr", ] +[[package]] +name = "target-lexicon" +version = "0.12.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ae9980cab1db3fceee2f6c6f643d5d8de2997c58ee8d25fb0cc8a9e9e7348e5" + [[package]] name = "tauri" version = "1.2.3" diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index 3c09e75..8cfb640 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -37,14 +37,15 @@ image = "0.24.5" mdns = "3.0.0" macos-app-nap = "0.0.1" ddc-hi = "0.4.1" +redb = "0.13.0" [features] # by default Tauri runs in production mode # when `tauri dev` runs it is executed with `cargo run --no-default-features` if `devPath` is an URL -default = [ "custom-protocol" ] +default = ["custom-protocol"] # this feature is used used for production builds where `devPath` points to the filesystem # DO NOT remove this -custom-protocol = [ "tauri/custom-protocol" ] +custom-protocol = ["tauri/custom-protocol"] [dev-dependencies] test_dir = "0.2.0" diff --git a/src-tauri/src/db/db.rs b/src-tauri/src/db/db.rs new file mode 100644 index 0000000..88d051a --- /dev/null +++ b/src-tauri/src/db/db.rs @@ -0,0 +1,25 @@ +use std::env::current_dir; + +use once_cell::sync::OnceCell; +use redb::Database; +use tauri::api::path::config_dir; + +use crate::picker; + +trait GlobalDatabase { + fn global() -> &'static T; +} + +impl GlobalDatabase for Database { + fn global() -> &'static Database { + static GLOBAL_DATABASE: OnceCell = OnceCell::new(); + + GLOBAL_DATABASE.get_or_init(|| { + let path = config_dir() + .unwrap_or(current_dir().unwrap()) + .join("main.redb"); + let db = Database::create(path).unwrap(); + return db; + }) + } +} diff --git a/src-tauri/src/db/mod.rs b/src-tauri/src/db/mod.rs new file mode 100644 index 0000000..5c91515 --- /dev/null +++ b/src-tauri/src/db/mod.rs @@ -0,0 +1,3 @@ +mod db; + +pub use db::*; diff --git a/src-tauri/src/display/brightness.rs b/src-tauri/src/display/brightness.rs index c45857e..2a24ff1 100644 --- a/src-tauri/src/display/brightness.rs +++ b/src-tauri/src/display/brightness.rs @@ -2,7 +2,7 @@ use serde::{Deserialize, Serialize}; #[derive(Clone, Copy, Serialize, Deserialize, Debug)] pub enum Brightness { - Relative(u16), + Relative(i16), Absolute(u16), } diff --git a/src-tauri/src/display/display_config.rs b/src-tauri/src/display/display_config.rs new file mode 100644 index 0000000..b14c0c4 --- /dev/null +++ b/src-tauri/src/display/display_config.rs @@ -0,0 +1,36 @@ +use std::time::SystemTime; + +use serde::{Deserialize, Serialize}; + +#[derive(Clone, Copy, Serialize, Deserialize, Debug)] +pub struct DisplayConfig { + pub id: usize, + pub brightness: u16, + pub max_brightness: u16, + pub min_brightness: u16, + pub contrast: u16, + pub max_contrast: u16, + pub min_contrast: u16, + pub mode: u16, + pub max_mode: u16, + pub min_mode: u16, + pub last_modified_at: SystemTime, +} + +impl DisplayConfig { + pub fn default(index: usize) -> Self { + Self { + id: index, + brightness: 30, + contrast: 50, + mode: 0, + last_modified_at: SystemTime::now(), + max_brightness: 100, + min_brightness: 0, + max_contrast: 100, + min_contrast: 0, + max_mode: 15, + min_mode: 0, + } + } +} diff --git a/src-tauri/src/display/manager.rs b/src-tauri/src/display/manager.rs index 724da4e..b793cf5 100644 --- a/src-tauri/src/display/manager.rs +++ b/src-tauri/src/display/manager.rs @@ -1,78 +1,175 @@ +use std::{ + borrow::Borrow, + collections::HashMap, + ops::Sub, + sync::Arc, + time::{Duration, SystemTime}, +}; + use ddc_hi::Display; use paris::{error, info}; -use tokio::sync::{broadcast, OnceCell}; +use tauri::async_runtime::Mutex; +use tokio::sync::{broadcast, OwnedMutexGuard}; +use tracing::warn; use crate::{display::Brightness, rpc}; -use super::DisplayBrightness; +use super::{display_config::DisplayConfig, DisplayBrightness}; use ddc_hi::Ddc; -pub struct Manager {} +pub struct Manager { + displays: Arc>>>>, +} impl Manager { - pub async fn global() -> &'static Self { - static DISPLAY_MANAGER: OnceCell = OnceCell::const_new(); + pub fn global() -> &'static Self { + static DISPLAY_MANAGER: once_cell::sync::OnceCell = + once_cell::sync::OnceCell::new(); - DISPLAY_MANAGER.get_or_init(|| Self::create()).await + DISPLAY_MANAGER.get_or_init(|| Self::create()) } - pub async fn create() -> Self { + pub fn create() -> Self { + let instance = Self { + displays: Arc::new(Mutex::new(HashMap::new())), + }; + instance + } + + pub async fn subscribe_display_brightness(&self) { let rpc = rpc::Manager::global().await; - let rx = rpc.client().subscribe_change_display_brightness_rx(); - tokio::spawn(Self::subscribe_display_brightness(rx)); + let mut rx = rpc.client().subscribe_change_display_brightness_rx(); - Self {} - } - - pub async fn subscribe_display_brightness(mut rx: broadcast::Receiver) { loop { if let Ok(display_brightness) = rx.recv().await { - if let Err(err) = Self::set_display_brightness(display_brightness) { + if let Err(err) = self.set_display_brightness(display_brightness).await { error!("set_display_brightness failed. {:?}", err); } } } } - pub fn set_display_brightness(display_brightness: DisplayBrightness) -> anyhow::Result<()> { - match Display::enumerate().get_mut(display_brightness.display_index) { - Some(display) => match display.handle.get_vcp_feature(0x10) { - Ok(curr_brightness) => { - let curr = curr_brightness.value(); - info!("curr_brightness: {:?}", curr); - let mut target = match display_brightness.brightness { - Brightness::Relative(v) => v + curr, - Brightness::Absolute(v) => v, - }; - if target.gt(&curr_brightness.maximum()) { - target = curr_brightness.maximum(); - } else if target.lt(&0) { - target = 0; + fn read_display_config_by_ddc(index: usize) -> anyhow::Result { + let mut displays = Display::enumerate(); + match displays.get_mut(index) { + Some(display) => { + let mut config = DisplayConfig::default(index); + match display.handle.get_vcp_feature(0x10) { + Ok(value) => { + config.max_brightness = value.maximum(); + config.min_brightness = 0; + config.brightness = value.value(); } - display - .handle - .set_vcp_feature(0x10, target) - .map_err(|err| anyhow::anyhow!("can not set brightness. {:?}", err))?; + Err(_) => {} + }; + match display.handle.get_vcp_feature(0x12) { + Ok(value) => { + config.max_contrast = value.maximum(); + config.min_contrast = 0; + config.contrast = value.value(); + } + Err(_) => {} + }; + match display.handle.get_vcp_feature(0xdc) { + Ok(value) => { + config.max_mode = value.maximum(); + config.min_mode = 0; + config.mode = value.value(); + } + Err(_) => {} + }; + + Ok(config) + } + None => anyhow::bail!("display#{} is missed.", index), + } + } + + async fn get_display(&self, index: usize) -> anyhow::Result> { + let mut displays = self.displays.lock().await; + match displays.get_mut(&index) { + Some(config) => { + let mut config = config.to_owned().lock_owned().await; + if config.last_modified_at > SystemTime::now().sub(Duration::from_secs(10)) { + info!("cached"); + return Ok(config); } - Err(err) => { - info!( - "can not get display#{} brightness. {:?}", - display_brightness.display_index, err - ); - if let Brightness::Absolute(v) = display_brightness.brightness { + return match Self::read_display_config_by_ddc(index) { + Ok(config) => { + let id = config.id; + let value = Arc::new(Mutex::new(config)); + let valueGuard = value.clone().lock_owned().await; + displays.insert(id, value); + info!("read form ddc"); + Ok(valueGuard) + } + Err(err) => { + warn!( + "can not read config from display by ddc, use CACHED value. {:?}", + err + ); + config.last_modified_at = SystemTime::now(); + Ok(config) + } + }; + } + None => { + let config = Self::read_display_config_by_ddc(index).map_err(|err| { + anyhow::anyhow!( + "can not read config from display by ddc,use DEFAULT value. {:?}", + err + ) + })?; + let id = config.id; + let value = Arc::new(Mutex::new(config)); + let valueGuard = value.clone().lock_owned().await; + displays.insert(id, value); + Ok(valueGuard) + } + } + } + + pub async fn set_display_brightness( + &self, + display_brightness: DisplayBrightness, + ) -> anyhow::Result<()> { + match Display::enumerate().get_mut(display_brightness.display_index) { + Some(display) => { + match self.get_display(display_brightness.display_index).await { + Ok(mut config) => { + let curr = config.brightness; + info!("curr_brightness: {:?}", curr); + let mut target = match display_brightness.brightness { + Brightness::Relative(v) => curr.wrapping_add_signed(v), + Brightness::Absolute(v) => v, + }; + if target.gt(&config.max_brightness) { + target = config.max_brightness; + } else if target.lt(&config.min_brightness) { + target = config.min_brightness; + } + config.brightness = target; display .handle - .set_vcp_feature(0x10, v) + .set_vcp_feature(0x10, target as u16) .map_err(|err| anyhow::anyhow!("can not set brightness. {:?}", err))?; - }; - } - }, + } + Err(err) => { + info!( + "can not get display#{} brightness. {:?}", + display_brightness.display_index, err + ); + if let Brightness::Absolute(v) = display_brightness.brightness { + display.handle.set_vcp_feature(0x10, v).map_err(|err| { + anyhow::anyhow!("can not set brightness. {:?}", err) + })?; + }; + } + }; + } None => { - anyhow::bail!( - "display#{} was not found.", - display_brightness.display_index - ); + warn!("display#{} is not found.", display_brightness.display_index); } } Ok(()) diff --git a/src-tauri/src/display/mod.rs b/src-tauri/src/display/mod.rs index 1509dfb..1110883 100644 --- a/src-tauri/src/display/mod.rs +++ b/src-tauri/src/display/mod.rs @@ -1,5 +1,6 @@ mod brightness; mod manager; +mod display_config; pub use brightness::*; pub use manager::*; diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs index b9a4ea5..5b2c499 100644 --- a/src-tauri/src/main.rs +++ b/src-tauri/src/main.rs @@ -5,6 +5,7 @@ #![feature(bool_to_option)] mod core; +mod db; mod display; mod picker; mod rpc; @@ -92,7 +93,10 @@ async fn play_mode(target_mode: AmbientLightMode) { } #[tokio::main] -async fn main() {display::Manager::global().await; +async fn main() { + let displayManager = display::Manager::global(); + tokio::spawn(displayManager.subscribe_display_brightness()); + tauri::Builder::default() .invoke_handler(tauri::generate_handler![ take_snapshot, diff --git a/src-tauri/src/rpc/mqtt.rs b/src-tauri/src/rpc/mqtt.rs index 2da0940..72697db 100644 --- a/src-tauri/src/rpc/mqtt.rs +++ b/src-tauri/src/rpc/mqtt.rs @@ -31,7 +31,7 @@ impl MqttRpc { match eventloop.poll().await { Ok(notification) => { let handled = || -> anyhow::Result<()> { - println!("MQTT notification = {:?}", notification); + // println!("MQTT notification = {:?}", notification); if let Event::Incoming(notification) = notification { if let Incoming::Publish(notification) = notification { match notification.topic.as_str() {