From 6c3ce607e0f2e0dce5265124e46a56a4ef98ee3b Mon Sep 17 00:00:00 2001 From: Ivan Li Date: Wed, 5 Apr 2023 12:25:14 +0800 Subject: [PATCH] =?UTF-8?q?pref:=20=E9=92=88=E5=AF=B9=20HiDPI=20=E5=B1=8F?= =?UTF-8?q?=E5=B9=95=E6=8D=95=E8=8E=B7=E7=9A=84=E4=BC=98=E5=8C=96=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src-tauri/Cargo.lock | 1 + src-tauri/Cargo.toml | 1 + src-tauri/src/ambient_light/config_manager.rs | 2 + src-tauri/src/ambient_light/publisher.rs | 452 +++++++++++++----- src-tauri/src/main.rs | 2 +- src-tauri/src/screenshot.rs | 82 ++-- src-tauri/src/screenshot_manager.rs | 133 ++++-- src/App.tsx | 2 +- src/components/led-strip-part.tsx | 12 +- src/components/led-strip-parts-sorter.tsx | 11 +- src/stores/led-strip.store.tsx | 2 +- 11 files changed, 486 insertions(+), 214 deletions(-) diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index d4b91ce..8d0bb61 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -2790,6 +2790,7 @@ version = "0.0.0" dependencies = [ "anyhow", "color_space", + "core-foundation", "core-graphics", "display-info", "env_logger", diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index edd8ba8..0ac9139 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -31,6 +31,7 @@ toml = "0.7.3" paho-mqtt = "0.12.1" time = {version="0.3.20", features= ["formatting"] } itertools = "0.10.5" +core-foundation = "0.9.3" [features] # this feature is used for production builds or when `devPath` points to the filesystem diff --git a/src-tauri/src/ambient_light/config_manager.rs b/src-tauri/src/ambient_light/config_manager.rs index aea3fa9..55cf64c 100644 --- a/src-tauri/src/ambient_light/config_manager.rs +++ b/src-tauri/src/ambient_light/config_manager.rs @@ -21,6 +21,8 @@ impl ConfigManager { let configs = LedStripConfigGroup::read_config().await.unwrap(); let (config_update_sender, config_update_receiver) = tokio::sync::watch::channel(configs.clone()); + + config_update_sender.send(configs.clone()).unwrap(); ConfigManager { config: Arc::new(RwLock::new(configs)), config_update_receiver, diff --git a/src-tauri/src/ambient_light/publisher.rs b/src-tauri/src/ambient_light/publisher.rs index 5da4049..2b1ff03 100644 --- a/src-tauri/src/ambient_light/publisher.rs +++ b/src-tauri/src/ambient_light/publisher.rs @@ -1,26 +1,31 @@ use std::{collections::HashMap, sync::Arc, time::Duration}; -use paris::{info, warn}; -use tauri::async_runtime::{Mutex, RwLock}; -use tokio::{sync::watch, time::sleep}; +use paris::warn; +use tauri::async_runtime::RwLock; +use tokio::{ + sync::{broadcast, watch}, + time::sleep, +}; use crate::{ ambient_light::{config, ConfigManager}, - led_color::LedColor, rpc::MqttRpc, - screenshot::{self, Screenshot}, - screenshot_manager::ScreenshotManager, + screenshot::LedSamplePoints, + screenshot_manager::{self, ScreenshotManager}, }; use itertools::Itertools; -use super::{LedStripConfigGroup, SamplePointConfig}; +use super::{LedStripConfigGroup, SamplePointConfig, SamplePointMapper}; pub struct LedColorsPublisher { sorted_colors_rx: Arc>>>, sorted_colors_tx: Arc>>>, - colors_rx: Arc>>>, - colors_tx: Arc>>>, + colors_rx: Arc>>>, + colors_tx: Arc>>>, + display_colors_rx: Arc)>>>, + display_colors_tx: Arc)>>>, + inner_tasks_version: Arc>, } impl LedColorsPublisher { @@ -30,6 +35,7 @@ impl LedColorsPublisher { let (sorted_tx, sorted_rx) = watch::channel(Vec::new()); let (tx, rx) = watch::channel(Vec::new()); + let (display_colors_tx, display_colors_rx) = broadcast::channel(8); LED_COLORS_PUBLISHER_GLOBAL .get_or_init(|| async { @@ -38,23 +44,172 @@ impl LedColorsPublisher { sorted_colors_tx: Arc::new(RwLock::new(sorted_tx)), colors_rx: Arc::new(RwLock::new(rx)), colors_tx: Arc::new(RwLock::new(tx)), + display_colors_rx: Arc::new(RwLock::new(display_colors_rx)), + display_colors_tx: Arc::new(RwLock::new(display_colors_tx)), + inner_tasks_version: Arc::new(RwLock::new(0)), } }) .await } - pub fn start(&self) { - let sorted_colors_tx = self.sorted_colors_tx.clone(); - let colors_tx = self.colors_tx.clone(); + fn start_one_display_colors_fetcher( + &self, + display_id: u32, + sample_points: Vec>, + ) { + let display_colors_tx = self.display_colors_tx.clone(); + let internal_tasks_version = self.inner_tasks_version.clone(); tokio::spawn(async move { + let display_colors_tx = display_colors_tx.read().await.clone(); + + let colors = screenshot_manager::get_display_colors(display_id, &sample_points); + + if let Err(err) = colors { + warn!("Failed to get colors: {}", err); + return; + } + + let mut start: tokio::time::Instant = tokio::time::Instant::now(); + let mut interval = tokio::time::interval(Duration::from_millis(66)); + let init_version = internal_tasks_version.read().await.clone(); + loop { + interval.tick().await; + tokio::time::sleep(Duration::from_millis(1)).await; + + if internal_tasks_version.read().await.clone() != init_version { + log::info!( + "inner task version changed, stop. {} != {}", + internal_tasks_version.read().await.clone(), + init_version + ); + + break; + } + + // log::info!("tick: {}ms", start.elapsed().as_millis()); + start = tokio::time::Instant::now(); + let colors = screenshot_manager::get_display_colors(display_id, &sample_points); + + if let Err(err) = colors { + warn!("Failed to get colors: {}", err); + sleep(Duration::from_millis(100)).await; + continue; + } + + let colors = colors.unwrap(); + + let color_len = colors.len(); + + match display_colors_tx.send(( + display_id, + colors + .into_iter() + .map(|color| color.get_rgb()) + .flatten() + .collect::>(), + )) { + Ok(_) => { + // log::info!("sent colors: {:?}", color_len); + } + Err(err) => { + warn!("Failed to send display_colors: {}", err); + } + }; + } + }); + } + + fn start_all_colors_worker(&self, display_ids: Vec, mappers: Vec) { + let sorted_colors_tx = self.sorted_colors_tx.clone(); + let colors_tx = self.colors_tx.clone(); + let display_colors_rx = self.display_colors_rx.clone(); + + tokio::spawn(async move { + for _ in 0..10 { + let mut rx = display_colors_rx.read().await.resubscribe(); + let sorted_colors_tx = sorted_colors_tx.write().await; let colors_tx = colors_tx.write().await; - let screenshot_manager = ScreenshotManager::global().await; - let config_manager = ConfigManager::global().await; - let config_receiver = config_manager.clone_config_update_receiver(); + let mut all_colors: Vec>> = vec![None; display_ids.len()]; + let mut start: tokio::time::Instant = tokio::time::Instant::now(); + + log::info!("start all_colors_worker"); + loop { + // log::info!("display_colors_rx changed"); + let color_info = rx.recv().await; + + if let Err(err) = color_info { + match err { + broadcast::error::RecvError::Closed => { + break; + } + broadcast::error::RecvError::Lagged(_) => { + warn!("display_colors_rx lagged"); + continue; + } + } + } + let (display_id, colors) = color_info.unwrap(); + + let index = display_ids.iter().position(|id| *id == display_id); + + if index.is_none() { + warn!("display id not found"); + continue; + } + + all_colors[index.unwrap()] = Some(colors); + + if all_colors.iter().all(|color| color.is_some()) { + let flatten_colors = all_colors + .clone() + .into_iter() + .flat_map(|c| c.unwrap()) + .collect::>(); + + match colors_tx.send(flatten_colors.clone()) { + Ok(_) => {} + Err(err) => { + warn!("Failed to send colors: {}", err); + } + }; + + let sorted_colors = + ScreenshotManager::get_sorted_colors(&flatten_colors, &mappers); + + match sorted_colors_tx.send(sorted_colors) { + Ok(_) => {} + Err(err) => { + warn!("Failed to send sorted colors: {}", err); + } + }; + log::info!("tick: {}ms", start.elapsed().as_millis()); + start = tokio::time::Instant::now(); + } + } + } + }); + } + + pub fn start(&self) { + let inner_tasks_version = self.inner_tasks_version.clone(); + + tokio::spawn(async move { + let publisher = Self::global().await; + + let mut inner_tasks_version = inner_tasks_version.write().await; + *inner_tasks_version = inner_tasks_version.overflowing_add(1).0; + + let config_manager = ConfigManager::global().await; + let mut config_receiver = config_manager.clone_config_update_receiver(); + + log::info!("waiting for config update..."); + + while config_receiver.changed().await.is_ok() { + log::info!("config updated, restart inner tasks..."); let configs = config_receiver.borrow().clone(); let configs = Self::get_colors_configs(&configs).await; @@ -66,73 +221,113 @@ impl LedColorsPublisher { let configs = configs.unwrap(); - let mut merged_screenshot_receiver = - screenshot_manager.clone_merged_screenshot_rx().await; + for sample_point_group in configs.sample_point_groups.clone() { + let display_id = sample_point_group.display_id; + let sample_points = sample_point_group.points; - let mut screenshots = HashMap::new(); - - loop { - let screenshot = merged_screenshot_receiver.recv().await; - - if let Err(err) = screenshot { - match err { - tokio::sync::broadcast::error::RecvError::Closed => { - warn!("closed"); - continue; - }, - tokio::sync::broadcast::error::RecvError::Lagged(_) => { - warn!("lagged"); - continue; - }, - } - } - - let screenshot = screenshot.unwrap(); - // log::info!("got screenshot: {:?}", screenshot.display_id); - - screenshots.insert(screenshot.display_id, screenshot); - - if screenshots.len() == configs.sample_point_groups.len() { - { - let screenshots = configs - .sample_point_groups - .iter() - .map(|strip| screenshots.get(&strip.display_id).unwrap()) - .collect::>(); - - let colors = screenshot_manager - .get_all_colors(&configs.sample_point_groups, &screenshots) - .await; - - let sorted_colors = - ScreenshotManager::get_sorted_colors(&colors, &configs.mappers) - .await; - - match colors_tx.send(colors) { - Ok(_) => { - // log::info!("colors updated"); - } - Err(_) => { - warn!("colors update failed"); - } - } - - match sorted_colors_tx.send(sorted_colors) { - Ok(_) => { - // log::info!("colors updated"); - } - Err(_) => { - warn!("colors update failed"); - } - } - } - - screenshots.clear(); - } + publisher.start_one_display_colors_fetcher(display_id, sample_points); } + + let display_ids = configs.sample_point_groups; + publisher.start_all_colors_worker( + display_ids.iter().map(|c| c.display_id).collect(), + configs.mappers, + ); + + break; } }); + // tokio::spawn(async move { + // loop { + // let sorted_colors_tx = sorted_colors_tx.write().await; + // let colors_tx = colors_tx.write().await; + // let screenshot_manager = ScreenshotManager::global().await; + + // let config_manager = ConfigManager::global().await; + // let config_receiver = config_manager.clone_config_update_receiver(); + // let configs = config_receiver.borrow().clone(); + // let configs = Self::get_colors_configs(&configs).await; + + // if let Err(err) = configs { + // warn!("Failed to get configs: {}", err); + // sleep(Duration::from_millis(100)).await; + // continue; + // } + + // let configs = configs.unwrap(); + + // let mut merged_screenshot_receiver = + // screenshot_manager.clone_merged_screenshot_rx().await; + + // let mut screenshots = HashMap::new(); + + // // let mut start = tokio::time::Instant::now(); + + // loop { + // let screenshot = merged_screenshot_receiver.recv().await; + + // if let Err(err) = screenshot { + // match err { + // tokio::sync::broadcast::error::RecvError::Closed => { + // warn!("closed"); + // continue; + // } + // tokio::sync::broadcast::error::RecvError::Lagged(_) => { + // warn!("lagged"); + // continue; + // } + // } + // } + + // let screenshot = screenshot.unwrap(); + // // log::info!("got screenshot: {:?}", screenshot.display_id); + + // screenshots.insert(screenshot.display_id, screenshot); + + // if screenshots.len() == configs.sample_point_groups.len() { + // // log::info!("{}", start.elapsed().as_millis().to_string()); + // { + // let screenshots = configs + // .sample_point_groups + // .iter() + // .map(|strip| screenshots.get(&strip.display_id).unwrap()) + // .collect::>(); + + // let colors = screenshot_manager + // .get_all_colors(&configs.sample_point_groups, &screenshots) + // .await; + + // let sorted_colors = + // ScreenshotManager::get_sorted_colors(&colors, &configs.mappers) + // .await; + + // match colors_tx.send(colors) { + // Ok(_) => { + // // log::info!("colors updated"); + // } + // Err(_) => { + // warn!("colors update failed"); + // } + // } + + // match sorted_colors_tx.send(sorted_colors) { + // Ok(_) => { + // // log::info!("colors updated"); + // } + // Err(_) => { + // warn!("colors update failed"); + // } + // } + // } + + // // screenshots.clear(); + // // start = tokio::time::Instant::now(); + // } + // } + // } + // }); + let rx = self.sorted_colors_rx.clone(); tokio::spawn(async move { let mut rx = rx.read().await.clone(); @@ -149,7 +344,7 @@ impl LedColorsPublisher { match Self::send_colors(colors).await { Ok(_) => { - log::info!("colors sent. len: {}", len); + // log::info!("colors sent. len: {}", len); } Err(err) => { warn!("colors send failed: {}", err); @@ -173,8 +368,6 @@ impl LedColorsPublisher { ) -> anyhow::Result { let screenshot_manager = ScreenshotManager::global().await; - let channels = screenshot_manager.channels.read().await; - let display_ids = configs .strips .iter() @@ -184,62 +377,83 @@ impl LedColorsPublisher { let mappers = configs.mappers.clone(); - let mut local_rx_list = Vec::new(); let mut colors_configs = Vec::new(); - for display_id in display_ids.clone().iter() { - let display_id = *display_id; + let mut merged_screenshot_receiver = screenshot_manager.clone_merged_screenshot_rx().await; - let channel = channels.get(&display_id); - if channel.is_none() { - anyhow::bail!("no channel for display_id: {}", display_id); + let mut screenshots = HashMap::new(); + + loop { + log::info!("waiting merged screenshot..."); + let screenshot = merged_screenshot_receiver.recv().await; + + if let Err(err) = screenshot { + match err { + tokio::sync::broadcast::error::RecvError::Closed => { + warn!("closed"); + continue; + } + tokio::sync::broadcast::error::RecvError::Lagged(_) => { + warn!("lagged"); + continue; + } + } } - let channel_rx = channel.unwrap().clone(); + let screenshot = screenshot.unwrap(); + // log::info!("got screenshot: {:?}", screenshot.display_id); - local_rx_list.push(channel.unwrap().clone()); + screenshots.insert(screenshot.display_id, screenshot); - let led_strip_configs: Vec<_> = configs - .strips - .iter() - .filter(|c| c.display_id == display_id) - .collect(); + if screenshots.len() == display_ids.len() { + for display_id in display_ids { + let led_strip_configs: Vec<_> = configs + .strips + .iter() + .filter(|c| c.display_id == display_id) + .collect(); - if led_strip_configs.len() == 0 { - warn!("no led strip config for display_id: {}", display_id); - continue; + if led_strip_configs.len() == 0 { + warn!("no led strip config for display_id: {}", display_id); + continue; + } + + let screenshot = screenshots.get(&display_id).unwrap(); + log::debug!("screenshot updated: {:?}", display_id); + + let points: Vec<_> = led_strip_configs + .iter() + .map(|config| screenshot.get_sample_points(&config)) + .collect(); + + let colors_config = DisplaySamplePointGroup { display_id, points }; + + colors_configs.push(colors_config); + } + + return Ok(AllColorConfig { + sample_point_groups: colors_configs, + mappers, + // screenshot_receivers: local_rx_list, + }); } - let rx = channel_rx.to_owned(); - - let screenshot = rx.borrow().clone(); - log::debug!("screenshot updated: {:?}", display_id); - - let points: Vec<_> = led_strip_configs - .iter() - .map(|config| screenshot.get_sample_points(&config)) - .flatten() - .collect(); - - let colors_config = config::SamplePointConfig { display_id, points }; - - colors_configs.push(colors_config); } - - return Ok(AllColorConfig { - sample_point_groups: colors_configs, - mappers, - screenshot_receivers: local_rx_list, - }); } - pub async fn clone_colors_receiver(&self) -> watch::Receiver> { + pub async fn clone_colors_receiver(&self) -> watch::Receiver> { self.colors_rx.read().await.clone() } } #[derive(Debug)] pub struct AllColorConfig { - pub sample_point_groups: Vec, + pub sample_point_groups: Vec, pub mappers: Vec, - pub screenshot_receivers: Vec>, + // pub screenshot_receivers: Vec>, +} + +#[derive(Debug, Clone)] +pub struct DisplaySamplePointGroup { + pub display_id: u32, + pub points: Vec>, } diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs index 67f1466..6612ba9 100644 --- a/src-tauri/src/main.rs +++ b/src-tauri/src/main.rs @@ -106,7 +106,7 @@ async fn get_one_edge_colors( if let Some(rx) = channels.get(&display_id) { let rx = rx.clone(); let screenshot = rx.borrow().clone(); - let bytes = screenshot.bytes.read().await; + let bytes = screenshot.bytes.read().await.to_owned(); let colors = Screenshot::get_one_edge_colors(&sample_points, &bytes, screenshot.bytes_per_row); Ok(colors) diff --git a/src-tauri/src/screenshot.rs b/src-tauri/src/screenshot.rs index 9332566..22d24f7 100644 --- a/src-tauri/src/screenshot.rs +++ b/src-tauri/src/screenshot.rs @@ -1,8 +1,9 @@ -use std::iter; +use std::cell::RefCell; +use std::{iter, cell::Ref}; use std::sync::Arc; use serde::{Deserialize, Serialize}; -use tauri::async_runtime::RwLock; +use tauri::async_runtime::{RwLock, Mutex}; use crate::{ambient_light::LedStripConfig, led_color::LedColor}; @@ -14,9 +15,10 @@ pub struct Screenshot { pub bytes_per_row: usize, pub bytes: Arc>>, pub scale_factor: f32, - pub sample_points: ScreenSamplePoints, } +static SINGLE_AXIS_POINTS: usize = 5; + impl Screenshot { pub fn new( display_id: u32, @@ -25,7 +27,6 @@ impl Screenshot { bytes_per_row: usize, bytes: Vec, scale_factor: f32, - sample_points: ScreenSamplePoints, ) -> Self { Self { display_id, @@ -34,7 +35,6 @@ impl Screenshot { bytes_per_row, bytes: Arc::new(RwLock::new(bytes)), scale_factor, - sample_points, } } @@ -44,10 +44,10 @@ impl Screenshot { match config.border { crate::ambient_light::Border::Top => { - Self::get_one_edge_sample_points(height / 8, width, config.len, 5) + Self::get_one_edge_sample_points(height / 18, width, config.len, SINGLE_AXIS_POINTS) } crate::ambient_light::Border::Bottom => { - let points = Self::get_one_edge_sample_points(height / 9, width, config.len, 5); + let points = Self::get_one_edge_sample_points(height / 18, width, config.len, SINGLE_AXIS_POINTS); points .into_iter() .map(|groups| -> Vec { @@ -56,7 +56,7 @@ impl Screenshot { .collect() } crate::ambient_light::Border::Left => { - let points = Self::get_one_edge_sample_points(width / 16, height, config.len, 5); + let points = Self::get_one_edge_sample_points(width / 32, height, config.len, SINGLE_AXIS_POINTS); points .into_iter() .map(|groups| -> Vec { @@ -65,7 +65,7 @@ impl Screenshot { .collect() } crate::ambient_light::Border::Right => { - let points = Self::get_one_edge_sample_points(width / 16, height, config.len, 5); + let points = Self::get_one_edge_sample_points(width / 32, height, config.len, SINGLE_AXIS_POINTS); points .into_iter() .map(|groups| -> Vec { @@ -112,46 +112,6 @@ impl Screenshot { .collect() } - pub async fn get_colors(&self) -> DisplayColorsOfLedStrips { - let bitmap = self.bytes.read().await; - - let top = - Self::get_one_edge_colors(&self.sample_points.top, bitmap.as_ref(), self.bytes_per_row) - .into_iter() - .flat_map(|color| color.get_rgb()) - .collect(); - let bottom = Self::get_one_edge_colors( - &self.sample_points.bottom, - bitmap.as_ref(), - self.bytes_per_row, - ) - .into_iter() - .flat_map(|color| color.get_rgb()) - .collect(); - let left = Self::get_one_edge_colors( - &self.sample_points.left, - bitmap.as_ref(), - self.bytes_per_row, - ) - .into_iter() - .flat_map(|color| color.get_rgb()) - .collect(); - let right = Self::get_one_edge_colors( - &self.sample_points.right, - bitmap.as_ref(), - self.bytes_per_row, - ) - .into_iter() - .flat_map(|color| color.get_rgb()) - .collect(); - DisplayColorsOfLedStrips { - top, - bottom, - left, - right, - } - } - pub fn get_one_edge_colors( sample_points_of_leds: &Vec, bitmap: &Vec, @@ -176,6 +136,30 @@ impl Screenshot { colors } + pub fn get_one_edge_colors_by_cg_image( + sample_points_of_leds: &Vec, + bitmap: core_foundation::data::CFData, + bytes_per_row: usize, + ) -> Vec { + let mut colors = vec![]; + for led_points in sample_points_of_leds { + let mut r = 0.0; + let mut g = 0.0; + let mut b = 0.0; + let len = led_points.len() as f64; + for (x, y) in led_points { + // log::info!("x: {}, y: {}, bytes_per_row: {}", x, y, bytes_per_row); + let position = x * 4 + y * bytes_per_row; + b += bitmap[position] as f64; + g += bitmap[position + 1] as f64; + r += bitmap[position + 2] as f64; + } + let color = LedColor::new((r / len) as u8, (g / len) as u8, (b / len) as u8); + colors.push(color); + } + colors + } + pub async fn get_colors_by_sample_points( &self, points: &Vec, diff --git a/src-tauri/src/screenshot_manager.rs b/src-tauri/src/screenshot_manager.rs index 0671977..b91fdf2 100644 --- a/src-tauri/src/screenshot_manager.rs +++ b/src-tauri/src/screenshot_manager.rs @@ -1,13 +1,16 @@ +use std::cell::{Ref, RefCell}; use std::{collections::HashMap, sync::Arc}; use core_graphics::display::{ kCGNullWindowID, kCGWindowImageDefault, kCGWindowListOptionOnScreenOnly, CGDisplay, }; +use core_graphics::geometry::{CGPoint, CGRect, CGSize}; use paris::warn; use tauri::async_runtime::RwLock; use tokio::sync::{broadcast, watch, OnceCell}; use tokio::time::{self, Duration}; +use crate::screenshot::LedSamplePoints; use crate::{ ambient_light::{SamplePointConfig, SamplePointMapper}, led_color::LedColor, @@ -16,7 +19,6 @@ use crate::{ pub fn take_screenshot(display_id: u32, scale_factor: f32) -> anyhow::Result { log::debug!("take_screenshot"); - // let start_at = std::time::Instant::now(); let cg_display = CGDisplay::new(display_id); let cg_image = CGDisplay::screenshot( @@ -26,7 +28,6 @@ pub fn take_screenshot(display_id: u32, scale_factor: f32) -> anyhow::Result anyhow::Result anyhow::Result>, +) -> anyhow::Result> { + log::debug!("take_screenshot"); + let cg_display = CGDisplay::new(display_id); + + let mut colors = vec![]; + let start_at = std::time::Instant::now(); + for points in sample_points { + if points.len() == 0 { + continue; + } + let start_x = points[0][0].0; + let start_y = points[0][0].1; + let end_x = points.last().unwrap().last().unwrap().0; + let end_y = points.last().unwrap().last().unwrap().1; + + let (start_x, end_x) = (usize::min(start_x, end_x), usize::max(start_x, end_x)); + let (start_y, end_y) = (usize::min(start_y, end_y), usize::max(start_y, end_y)); + + let origin = CGPoint { + x: start_x as f64 + cg_display.bounds().origin.x, + y: start_y as f64 + cg_display.bounds().origin.y, + }; + let size = CGSize { + width: (end_x - start_x + 1) as f64, + height: (end_y - start_y + 1) as f64, + }; + + let cg_image = CGDisplay::screenshot( + CGRect::new(&origin, &size), + kCGWindowListOptionOnScreenOnly, + kCGNullWindowID, + kCGWindowImageDefault, + ) + .ok_or_else(|| anyhow::anyhow!("Display#{}: take screenshot failed", display_id))?; + + let bitmap = cg_image.data(); + + let points = points + .iter() + .map(|points| { + points + .iter() + .map(|(x, y)| (*x - start_x, *y - start_y)) + .collect::>() + }) + .collect::>(); + + let mut part_colors = + Screenshot::get_one_edge_colors_by_cg_image(&points, bitmap, cg_image.bytes_per_row()); + colors.append(&mut part_colors); + } + + // if display_id == 4849664 { + // log::info!( + // "======= get_display_colors {} took {}ms", + // display_id, + // start_at.elapsed().as_millis() + // ); + // } + Ok(colors) +} + pub struct ScreenshotManager { pub channels: Arc>>>, - merged_screenshot_rx: Arc>>, merged_screenshot_tx: Arc>>, } @@ -66,10 +124,9 @@ impl ScreenshotManager { SCREENSHOT_MANAGER .get_or_init(|| async { let channels = Arc::new(RwLock::new(HashMap::new())); - let (merged_screenshot_tx, merged_screenshot_rx) = broadcast::channel(2); + let (merged_screenshot_tx, _) = broadcast::channel::(2); Self { channels, - merged_screenshot_rx: Arc::new(RwLock::new(merged_screenshot_rx)), merged_screenshot_tx: Arc::new(RwLock::new(merged_screenshot_tx)), } }) @@ -94,7 +151,8 @@ impl ScreenshotManager { warn!("take_screenshot_loop: {}", screenshot.err().unwrap()); return; } - let mut interval = time::interval(Duration::from_millis(33)); + let mut interval = time::interval(Duration::from_millis(3300)); + let mut start = tokio::time::Instant::now(); let screenshot = screenshot.unwrap(); let (screenshot_tx, screenshot_rx) = watch::channel(screenshot); @@ -107,7 +165,7 @@ impl ScreenshotManager { let merged_screenshot_tx = merged_screenshot_tx.read().await.clone(); loop { - // interval.tick().await; + start = tokio::time::Instant::now(); Self::take_screenshot_loop( display_id, scale_factor, @@ -115,7 +173,8 @@ impl ScreenshotManager { &merged_screenshot_tx, ) .await; - tokio::time::sleep(Duration::from_millis(20)).await; + interval.tick().await; + tokio::time::sleep(Duration::from_millis(1)).await; } }); @@ -130,9 +189,14 @@ impl ScreenshotManager { ) { let screenshot = take_screenshot(display_id, scale_factor); if let Ok(screenshot) = screenshot { - screenshot_tx.send(screenshot.clone()).unwrap(); - merged_screenshot_tx.send(screenshot).unwrap(); - log::debug!("take_screenshot_loop: send success. display#{}", display_id) + match merged_screenshot_tx.send(screenshot.clone()) { + Ok(_) => {} + Err(err) => { + // warn!("take_screenshot_loop: merged_screenshot_tx.send failed. display#{}. err: {}", display_id, err); + } + } + screenshot_tx.send(screenshot).unwrap(); + // log::info!("take_screenshot_loop: send success. display#{}", display_id) } else { warn!("take_screenshot_loop: {}", screenshot.err().unwrap()); } @@ -155,10 +219,7 @@ impl ScreenshotManager { all_colors } - pub async fn get_sorted_colors( - colors: &Vec, - mappers: &Vec, - ) -> Vec { + pub fn get_sorted_colors(colors: &Vec, mappers: &Vec) -> Vec { let total_leds = mappers .iter() .map(|mapper| usize::max(mapper.start, mapper.end)) @@ -178,33 +239,29 @@ impl ScreenshotManager { return; } - if color_index + group.start.abs_diff(group.end) > colors.len() { + if color_index + group.start.abs_diff(group.end) * 3 > colors.len(){ warn!( "get_sorted_colors: color_index out of range. color_index: {}, strip len: {}, colors.len(): {}", - color_index, + color_index / 3, group.start.abs_diff(group.end), - colors.len() + colors.len() / 3 ); return; } if group.end > group.start { for i in group.start..group.end { - let rgb = colors[color_index].get_rgb(); - color_index += 1; - - global_colors[i * 3] = rgb[0]; - global_colors[i * 3 + 1] = rgb[1]; - global_colors[i * 3 + 2] = rgb[2]; + global_colors[i * 3] = colors[color_index +0]; + global_colors[i * 3 + 1] = colors[color_index +1]; + global_colors[i * 3 + 2] = colors[color_index +2]; + color_index += 3; } } else { for i in (group.end..group.start).rev() { - let rgb = colors[color_index].get_rgb(); - color_index += 1; - - global_colors[i * 3] = rgb[0]; - global_colors[i * 3 + 1] = rgb[1]; - global_colors[i * 3 + 2] = rgb[2]; + global_colors[i * 3] = colors[color_index +0]; + global_colors[i * 3 + 1] = colors[color_index +1]; + global_colors[i * 3 + 2] = colors[color_index +2]; + color_index += 3; } } }); diff --git a/src/App.tsx b/src/App.tsx index b755b29..89c3f20 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -44,7 +44,7 @@ function App() { // listen to led_colors_changed event createEffect(() => { - const unlisten = listen>('led_colors_changed', (event) => { + const unlisten = listen('led_colors_changed', (event) => { const colors = event.payload; setLedStripStore({ diff --git a/src/components/led-strip-part.tsx b/src/components/led-strip-part.tsx index 274c1a6..7ffbdf1 100644 --- a/src/components/led-strip-part.tsx +++ b/src/components/led-strip-part.tsx @@ -1,5 +1,4 @@ import { invoke } from '@tauri-apps/api'; -import { listen } from '@tauri-apps/api/event'; import { Component, createEffect, @@ -8,7 +7,6 @@ import { createSignal, For, JSX, - onCleanup, splitProps, useContext, } from 'solid-js'; @@ -77,9 +75,15 @@ export const LedStripPart: Component = (props) => { return; } - const offset = mapper.pos; + const offset = mapper.pos * 3; + + const colors = new Array(localProps.config.len).fill(null).map((_, i) => { + const index = offset + i * 3; + return `rgb(${ledStripStore.colors[index]}, ${ledStripStore.colors[index + 1]}, ${ + ledStripStore.colors[index + 2] + })`; + }); - const colors = ledStripStore.colors.slice(offset, offset + localProps.config.len); setColors(colors); }); diff --git a/src/components/led-strip-parts-sorter.tsx b/src/components/led-strip-parts-sorter.tsx index 3d8c442..cb01e42 100644 --- a/src/components/led-strip-parts-sorter.tsx +++ b/src/components/led-strip-parts-sorter.tsx @@ -115,12 +115,21 @@ const SorterItem: Component<{ strip: LedStripConfig; mapper: LedStripPixelMapper // update fullLeds createEffect(() => { const fullLeds = new Array(ledStripStore.totalLedCount).fill(null); + const colors = ledStripStore.colors; const { start, end, pos } = props.mapper; const isForward = start < end; const step = isForward ? 1 : -1; for (let i = start, j = pos; i !== end; i += step, j++) { - fullLeds[i] = ledStripStore.colors[j]; + let c1 = `rgb(${Math.floor(colors[j * 3] * 0.8)}, ${Math.floor( + colors[j * 3 + 1] * 0.8, + )}, ${Math.floor(colors[j * 3 + 2] * 0.8)})`; + let c2 = `rgb(${Math.min(Math.floor(colors[j * 3] * 1.2), 255)}, ${Math.min( + Math.floor(colors[j * 3 + 1] * 1.2), + 255, + )}, ${Math.min(Math.floor(colors[j * 3 + 2] * 1.2), 255)})`; + + fullLeds[i] = `linear-gradient(70deg, ${c1} 10%, ${c2})`; } setFullLeds(fullLeds); diff --git a/src/stores/led-strip.store.tsx b/src/stores/led-strip.store.tsx index 35153c0..754838a 100644 --- a/src/stores/led-strip.store.tsx +++ b/src/stores/led-strip.store.tsx @@ -4,7 +4,7 @@ import { LedStripConfig, LedStripPixelMapper } from '../models/led-strip-config' export const [ledStripStore, setLedStripStore] = createStore({ strips: new Array(), mappers: new Array(), - colors: new Array(), + colors: new Uint8ClampedArray(), sortedColors: new Uint8ClampedArray(), get totalLedCount() { return Math.max(0, ...ledStripStore.mappers.map((m) => Math.max(m.start, m.end)));