diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index add173e..02de222 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -141,6 +141,7 @@ dependencies = [ "serde_with", "tauri", "tauri-build", + "tokio", "windows 0.52.0", "windows-version", ] diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index e0ca2c1..fff9b5f 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -12,6 +12,7 @@ raw-window-handle = "0.5" serde = { version = "1.0", features = ["derive"] } serde_with = { version = "3.0", features = ["base64"] } tauri = { version = "1.5.x", features = ["wry", "window-set-focus", "window-set-position", "window-set-size", "window-show", "window-hide", "system-tray"], default-features = false } +tokio = { version = "1.0", features = ["time"] } windows-version = "0.1" [dependencies.windows] diff --git a/src-tauri/src/monitors.rs b/src-tauri/src/monitors.rs index e378e25..97d1655 100644 --- a/src-tauri/src/monitors.rs +++ b/src-tauri/src/monitors.rs @@ -2,13 +2,14 @@ use std::ffi::OsStr; use monitor::{Feature, Interface, Monitor}; use serde::{Deserialize, Serialize}; -use tauri::async_runtime::RwLock; +use tauri::async_runtime::{Mutex, RwLock}; use tauri::State; +use tokio::time::{sleep, sleep_until, Duration, Instant}; use crate::util::JSResult; #[derive(Debug)] -pub struct Monitors(RwLock>); +pub struct Monitors(RwLock)>>); impl Monitors { pub const fn new() -> Monitors { @@ -20,8 +21,9 @@ impl Monitors { pub async fn refresh_monitors(monitors: State<'_, Monitors>) -> JSResult<()> { let mut monitors = monitors.0.write().await; monitors.clear(); + let stub_instant = Instant::now(); for monitor in monitor::get_monitors() { - monitors.push(monitor); + monitors.push((monitor, Mutex::const_new(stub_instant))); } Ok(()) } @@ -31,15 +33,18 @@ pub async fn get_monitors(monitors: State<'_, Monitors>) -> JSResult let monitors = monitors.0.read().await; Ok(monitors .iter() - .map(|monitor| monitor.id.to_string_lossy().into_owned()) + .map(|(monitor, _)| monitor.id.to_string_lossy().into_owned()) .collect()) } -fn get_monitor_by_id<'a>(monitors: &'a [Monitor], id: &'_ str) -> JSResult<&'a Monitor> { +fn get_monitor_by_id<'a>( + monitors: &'a [(Monitor, Mutex)], + id: &'_ str, +) -> JSResult<&'a (Monitor, Mutex)> { let id_os: &OsStr = id.as_ref(); monitors .iter() - .find(|monitor| monitor.id == id_os) + .find(|(monitor, _)| monitor.id == id_os) .ok_or_else(|| format!("no such monitor: '{id}'").into()) } @@ -49,7 +54,7 @@ pub async fn get_monitor_user_friendly_name( id: String, ) -> JSResult> { let monitors = monitors.0.read().await; - let monitor = get_monitor_by_id(&monitors, &id)?; + let monitor = &get_monitor_by_id(&monitors, &id)?.0; Ok(monitor .get_user_friendly_name()? .map(|s| s.to_string_lossy().into_owned())) @@ -74,6 +79,9 @@ pub struct Reply { source: &'static str, } +// TODO: move it into JS +const UPDATE_INTERVAL: Duration = Duration::from_millis(200); + #[tauri::command] pub async fn get_monitor_feature( monitors: State<'_, Monitors>, @@ -81,13 +89,18 @@ pub async fn get_monitor_feature( feature: String, ) -> JSResult { let monitors = monitors.0.read().await; - let monitor = get_monitor_by_id(&monitors, &id)?; + let (monitor, instant) = get_monitor_by_id(&monitors, &id)?; let feature = feature_from_string(feature)?; + + let mut instant = instant.lock().await; + sleep_until(*instant).await; let monitor::Reply { current, maximum, source, } = monitor.get_feature(feature)?; + *instant = Instant::now() + UPDATE_INTERVAL; + Ok(Reply { current, maximum, @@ -104,9 +117,29 @@ pub async fn set_monitor_feature( id: String, feature: String, value: u32, -) -> JSResult<()> { +) -> JSResult { let monitors = monitors.0.read().await; - let monitor = get_monitor_by_id(&monitors, &id)?; + let (monitor, instant) = get_monitor_by_id(&monitors, &id)?; let feature = feature_from_string(feature)?; - Ok(monitor.set_feature(feature, value)?) + + let mut instant = instant.lock().await; + sleep_until(*instant).await; + monitor.set_feature(feature, value)?; + + sleep(UPDATE_INTERVAL).await; + let monitor::Reply { + current, + maximum, + source, + } = monitor.get_feature(feature)?; + *instant = Instant::now() + UPDATE_INTERVAL; + + Ok(Reply { + current, + maximum, + source: match source { + Interface::DDCCI => "ddcci", + Interface::IOCTL => "ioctl", + }, + }) } diff --git a/src/components/FeatureSlider.vue b/src/components/FeatureSlider.vue index eeb0a5c..f3f713b 100644 --- a/src/components/FeatureSlider.vue +++ b/src/components/FeatureSlider.vue @@ -1,9 +1,8 @@ diff --git a/src/monitor.ts b/src/monitor.ts index 97f6698..15f0ed0 100644 --- a/src/monitor.ts +++ b/src/monitor.ts @@ -1,6 +1,5 @@ import { invoke } from "@tauri-apps/api"; import { reactive, toRaw, DeepReadonly } from "vue"; -import settings from "./settings"; export interface Reply { current: number; @@ -19,12 +18,6 @@ export interface Monitor { features: Feature[]; } -function timeout(millis: number): Promise { - return new Promise((resolve) => { - setTimeout(resolve, millis); - }); -} - export class Manager { readonly monitors: DeepReadonly = reactive([]); private refreshing = false; @@ -66,11 +59,7 @@ export class Manager { const featureNames = monitor.features.length ? monitor.features.map((feature) => feature.name) : ["luminance", "contrast", "brightness", "volume", "powerstate"]; - let first = true; for (const name of featureNames) { - if (!first) { - await timeout(settings.updateInterval); - } let value: Reply | undefined; try { value = await invoke("get_monitor_feature", { @@ -92,7 +81,6 @@ export class Manager { } else if (idx != -1) { monitor.features.splice(idx, 1); } - first = false; } })(), ); @@ -131,17 +119,12 @@ export class Manager { async setFeature(id: string, name: string, value: number): Promise { const feature = this.getFeature(id, name) as Feature; if (feature.value.current != value) { - await invoke("set_monitor_feature", { - id, - feature: name, - value, - }); - await timeout(settings.updateInterval); Object.assign( feature.value, - await invoke("get_monitor_feature", { + await invoke("set_monitor_feature", { id, feature: name, + value, }), ); }