pref: 缓存显示器参数读数。#5.

This commit is contained in:
Ivan Li 2023-02-18 14:47:41 +08:00
parent 070100cdbc
commit 550328ba1e
10 changed files with 245 additions and 51 deletions

27
src-tauri/Cargo.lock generated
View File

@ -856,6 +856,7 @@ dependencies = [
"mdns", "mdns",
"once_cell", "once_cell",
"paris", "paris",
"redb",
"rumqttc", "rumqttc",
"scrap", "scrap",
"serde", "serde",
@ -2732,6 +2733,16 @@ dependencies = [
"unicode-ident", "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]] [[package]]
name = "quick-error" name = "quick-error"
version = "1.2.3" version = "1.2.3"
@ -2868,6 +2879,16 @@ dependencies = [
"num_cpus", "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]] [[package]]
name = "redox_syscall" name = "redox_syscall"
version = "0.2.16" version = "0.2.16"
@ -3614,6 +3635,12 @@ dependencies = [
"xattr", "xattr",
] ]
[[package]]
name = "target-lexicon"
version = "0.12.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ae9980cab1db3fceee2f6c6f643d5d8de2997c58ee8d25fb0cc8a9e9e7348e5"
[[package]] [[package]]
name = "tauri" name = "tauri"
version = "1.2.3" version = "1.2.3"

View File

@ -37,14 +37,15 @@ image = "0.24.5"
mdns = "3.0.0" mdns = "3.0.0"
macos-app-nap = "0.0.1" macos-app-nap = "0.0.1"
ddc-hi = "0.4.1" ddc-hi = "0.4.1"
redb = "0.13.0"
[features] [features]
# by default Tauri runs in production mode # 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 # 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 # this feature is used used for production builds where `devPath` points to the filesystem
# DO NOT remove this # DO NOT remove this
custom-protocol = [ "tauri/custom-protocol" ] custom-protocol = ["tauri/custom-protocol"]
[dev-dependencies] [dev-dependencies]
test_dir = "0.2.0" test_dir = "0.2.0"

25
src-tauri/src/db/db.rs Normal file
View File

@ -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<T> {
fn global() -> &'static T;
}
impl GlobalDatabase<Database> for Database {
fn global() -> &'static Database {
static GLOBAL_DATABASE: OnceCell<Database> = 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;
})
}
}

3
src-tauri/src/db/mod.rs Normal file
View File

@ -0,0 +1,3 @@
mod db;
pub use db::*;

View File

@ -2,7 +2,7 @@ use serde::{Deserialize, Serialize};
#[derive(Clone, Copy, Serialize, Deserialize, Debug)] #[derive(Clone, Copy, Serialize, Deserialize, Debug)]
pub enum Brightness { pub enum Brightness {
Relative(u16), Relative(i16),
Absolute(u16), Absolute(u16),
} }

View File

@ -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,
}
}
}

View File

@ -1,78 +1,175 @@
use std::{
borrow::Borrow,
collections::HashMap,
ops::Sub,
sync::Arc,
time::{Duration, SystemTime},
};
use ddc_hi::Display; use ddc_hi::Display;
use paris::{error, info}; 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 crate::{display::Brightness, rpc};
use super::DisplayBrightness; use super::{display_config::DisplayConfig, DisplayBrightness};
use ddc_hi::Ddc; use ddc_hi::Ddc;
pub struct Manager {} pub struct Manager {
displays: Arc<Mutex<HashMap<usize, Arc<Mutex<DisplayConfig>>>>>,
}
impl Manager { impl Manager {
pub async fn global() -> &'static Self { pub fn global() -> &'static Self {
static DISPLAY_MANAGER: OnceCell<Manager> = OnceCell::const_new(); static DISPLAY_MANAGER: once_cell::sync::OnceCell<Manager> =
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 rpc = rpc::Manager::global().await;
let rx = rpc.client().subscribe_change_display_brightness_rx(); let mut rx = rpc.client().subscribe_change_display_brightness_rx();
tokio::spawn(Self::subscribe_display_brightness(rx));
Self {}
}
pub async fn subscribe_display_brightness(mut rx: broadcast::Receiver<DisplayBrightness>) {
loop { loop {
if let Ok(display_brightness) = rx.recv().await { 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); error!("set_display_brightness failed. {:?}", err);
} }
} }
} }
} }
pub fn set_display_brightness(display_brightness: DisplayBrightness) -> anyhow::Result<()> { fn read_display_config_by_ddc(index: usize) -> anyhow::Result<DisplayConfig> {
match Display::enumerate().get_mut(display_brightness.display_index) { let mut displays = Display::enumerate();
Some(display) => match display.handle.get_vcp_feature(0x10) { match displays.get_mut(index) {
Ok(curr_brightness) => { Some(display) => {
let curr = curr_brightness.value(); let mut config = DisplayConfig::default(index);
info!("curr_brightness: {:?}", curr); match display.handle.get_vcp_feature(0x10) {
let mut target = match display_brightness.brightness { Ok(value) => {
Brightness::Relative(v) => v + curr, config.max_brightness = value.maximum();
Brightness::Absolute(v) => v, config.min_brightness = 0;
}; config.brightness = value.value();
if target.gt(&curr_brightness.maximum()) {
target = curr_brightness.maximum();
} else if target.lt(&0) {
target = 0;
} }
display Err(_) => {}
.handle };
.set_vcp_feature(0x10, target) match display.handle.get_vcp_feature(0x12) {
.map_err(|err| anyhow::anyhow!("can not set brightness. {:?}", err))?; 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<OwnedMutexGuard<DisplayConfig>> {
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) => { return match Self::read_display_config_by_ddc(index) {
info!( Ok(config) => {
"can not get display#{} brightness. {:?}", let id = config.id;
display_brightness.display_index, err let value = Arc::new(Mutex::new(config));
); let valueGuard = value.clone().lock_owned().await;
if let Brightness::Absolute(v) = display_brightness.brightness { 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 display
.handle .handle
.set_vcp_feature(0x10, v) .set_vcp_feature(0x10, target as u16)
.map_err(|err| anyhow::anyhow!("can not set brightness. {:?}", err))?; .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 => { None => {
anyhow::bail!( warn!("display#{} is not found.", display_brightness.display_index);
"display#{} was not found.",
display_brightness.display_index
);
} }
} }
Ok(()) Ok(())

View File

@ -1,5 +1,6 @@
mod brightness; mod brightness;
mod manager; mod manager;
mod display_config;
pub use brightness::*; pub use brightness::*;
pub use manager::*; pub use manager::*;

View File

@ -5,6 +5,7 @@
#![feature(bool_to_option)] #![feature(bool_to_option)]
mod core; mod core;
mod db;
mod display; mod display;
mod picker; mod picker;
mod rpc; mod rpc;
@ -92,7 +93,10 @@ async fn play_mode(target_mode: AmbientLightMode) {
} }
#[tokio::main] #[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() tauri::Builder::default()
.invoke_handler(tauri::generate_handler![ .invoke_handler(tauri::generate_handler![
take_snapshot, take_snapshot,

View File

@ -31,7 +31,7 @@ impl MqttRpc {
match eventloop.poll().await { match eventloop.poll().await {
Ok(notification) => { Ok(notification) => {
let handled = || -> anyhow::Result<()> { let handled = || -> anyhow::Result<()> {
println!("MQTT notification = {:?}", notification); // println!("MQTT notification = {:?}", notification);
if let Event::Incoming(notification) = notification { if let Event::Incoming(notification) = notification {
if let Incoming::Publish(notification) = notification { if let Incoming::Publish(notification) = notification {
match notification.topic.as_str() { match notification.topic.as_str() {