pref: 针对 HiDPI 屏幕捕获的优化。
This commit is contained in:
parent
3ec983cd95
commit
6c3ce607e0
1
src-tauri/Cargo.lock
generated
1
src-tauri/Cargo.lock
generated
@ -2790,6 +2790,7 @@ version = "0.0.0"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"color_space",
|
"color_space",
|
||||||
|
"core-foundation",
|
||||||
"core-graphics",
|
"core-graphics",
|
||||||
"display-info",
|
"display-info",
|
||||||
"env_logger",
|
"env_logger",
|
||||||
|
@ -31,6 +31,7 @@ toml = "0.7.3"
|
|||||||
paho-mqtt = "0.12.1"
|
paho-mqtt = "0.12.1"
|
||||||
time = {version="0.3.20", features= ["formatting"] }
|
time = {version="0.3.20", features= ["formatting"] }
|
||||||
itertools = "0.10.5"
|
itertools = "0.10.5"
|
||||||
|
core-foundation = "0.9.3"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
# this feature is used for production builds or when `devPath` points to the filesystem
|
# this feature is used for production builds or when `devPath` points to the filesystem
|
||||||
|
@ -21,6 +21,8 @@ impl ConfigManager {
|
|||||||
let configs = LedStripConfigGroup::read_config().await.unwrap();
|
let configs = LedStripConfigGroup::read_config().await.unwrap();
|
||||||
let (config_update_sender, config_update_receiver) =
|
let (config_update_sender, config_update_receiver) =
|
||||||
tokio::sync::watch::channel(configs.clone());
|
tokio::sync::watch::channel(configs.clone());
|
||||||
|
|
||||||
|
config_update_sender.send(configs.clone()).unwrap();
|
||||||
ConfigManager {
|
ConfigManager {
|
||||||
config: Arc::new(RwLock::new(configs)),
|
config: Arc::new(RwLock::new(configs)),
|
||||||
config_update_receiver,
|
config_update_receiver,
|
||||||
|
@ -1,26 +1,31 @@
|
|||||||
use std::{collections::HashMap, sync::Arc, time::Duration};
|
use std::{collections::HashMap, sync::Arc, time::Duration};
|
||||||
|
|
||||||
use paris::{info, warn};
|
use paris::warn;
|
||||||
use tauri::async_runtime::{Mutex, RwLock};
|
use tauri::async_runtime::RwLock;
|
||||||
use tokio::{sync::watch, time::sleep};
|
use tokio::{
|
||||||
|
sync::{broadcast, watch},
|
||||||
|
time::sleep,
|
||||||
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
ambient_light::{config, ConfigManager},
|
ambient_light::{config, ConfigManager},
|
||||||
led_color::LedColor,
|
|
||||||
rpc::MqttRpc,
|
rpc::MqttRpc,
|
||||||
screenshot::{self, Screenshot},
|
screenshot::LedSamplePoints,
|
||||||
screenshot_manager::ScreenshotManager,
|
screenshot_manager::{self, ScreenshotManager},
|
||||||
};
|
};
|
||||||
|
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
|
|
||||||
use super::{LedStripConfigGroup, SamplePointConfig};
|
use super::{LedStripConfigGroup, SamplePointConfig, SamplePointMapper};
|
||||||
|
|
||||||
pub struct LedColorsPublisher {
|
pub struct LedColorsPublisher {
|
||||||
sorted_colors_rx: Arc<RwLock<watch::Receiver<Vec<u8>>>>,
|
sorted_colors_rx: Arc<RwLock<watch::Receiver<Vec<u8>>>>,
|
||||||
sorted_colors_tx: Arc<RwLock<watch::Sender<Vec<u8>>>>,
|
sorted_colors_tx: Arc<RwLock<watch::Sender<Vec<u8>>>>,
|
||||||
colors_rx: Arc<RwLock<watch::Receiver<Vec<LedColor>>>>,
|
colors_rx: Arc<RwLock<watch::Receiver<Vec<u8>>>>,
|
||||||
colors_tx: Arc<RwLock<watch::Sender<Vec<LedColor>>>>,
|
colors_tx: Arc<RwLock<watch::Sender<Vec<u8>>>>,
|
||||||
|
display_colors_rx: Arc<RwLock<broadcast::Receiver<(u32, Vec<u8>)>>>,
|
||||||
|
display_colors_tx: Arc<RwLock<broadcast::Sender<(u32, Vec<u8>)>>>,
|
||||||
|
inner_tasks_version: Arc<RwLock<usize>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl LedColorsPublisher {
|
impl LedColorsPublisher {
|
||||||
@ -30,6 +35,7 @@ impl LedColorsPublisher {
|
|||||||
|
|
||||||
let (sorted_tx, sorted_rx) = watch::channel(Vec::new());
|
let (sorted_tx, sorted_rx) = watch::channel(Vec::new());
|
||||||
let (tx, 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
|
LED_COLORS_PUBLISHER_GLOBAL
|
||||||
.get_or_init(|| async {
|
.get_or_init(|| async {
|
||||||
@ -38,23 +44,172 @@ impl LedColorsPublisher {
|
|||||||
sorted_colors_tx: Arc::new(RwLock::new(sorted_tx)),
|
sorted_colors_tx: Arc::new(RwLock::new(sorted_tx)),
|
||||||
colors_rx: Arc::new(RwLock::new(rx)),
|
colors_rx: Arc::new(RwLock::new(rx)),
|
||||||
colors_tx: Arc::new(RwLock::new(tx)),
|
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
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn start(&self) {
|
fn start_one_display_colors_fetcher(
|
||||||
let sorted_colors_tx = self.sorted_colors_tx.clone();
|
&self,
|
||||||
let colors_tx = self.colors_tx.clone();
|
display_id: u32,
|
||||||
|
sample_points: Vec<Vec<LedSamplePoints>>,
|
||||||
|
) {
|
||||||
|
let display_colors_tx = self.display_colors_tx.clone();
|
||||||
|
let internal_tasks_version = self.inner_tasks_version.clone();
|
||||||
|
|
||||||
tokio::spawn(async move {
|
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 {
|
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::<Vec<_>>(),
|
||||||
|
)) {
|
||||||
|
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<u32>, mappers: Vec<SamplePointMapper>) {
|
||||||
|
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 sorted_colors_tx = sorted_colors_tx.write().await;
|
||||||
let colors_tx = colors_tx.write().await;
|
let colors_tx = colors_tx.write().await;
|
||||||
let screenshot_manager = ScreenshotManager::global().await;
|
|
||||||
|
|
||||||
let config_manager = ConfigManager::global().await;
|
let mut all_colors: Vec<Option<Vec<u8>>> = vec![None; display_ids.len()];
|
||||||
let config_receiver = config_manager.clone_config_update_receiver();
|
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::<Vec<_>>();
|
||||||
|
|
||||||
|
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 = config_receiver.borrow().clone();
|
||||||
let configs = Self::get_colors_configs(&configs).await;
|
let configs = Self::get_colors_configs(&configs).await;
|
||||||
|
|
||||||
@ -66,73 +221,113 @@ impl LedColorsPublisher {
|
|||||||
|
|
||||||
let configs = configs.unwrap();
|
let configs = configs.unwrap();
|
||||||
|
|
||||||
let mut merged_screenshot_receiver =
|
for sample_point_group in configs.sample_point_groups.clone() {
|
||||||
screenshot_manager.clone_merged_screenshot_rx().await;
|
let display_id = sample_point_group.display_id;
|
||||||
|
let sample_points = sample_point_group.points;
|
||||||
|
|
||||||
let mut screenshots = HashMap::new();
|
publisher.start_one_display_colors_fetcher(display_id, sample_points);
|
||||||
|
|
||||||
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::<Vec<_>>();
|
|
||||||
|
|
||||||
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();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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::<Vec<_>>();
|
||||||
|
|
||||||
|
// 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();
|
let rx = self.sorted_colors_rx.clone();
|
||||||
tokio::spawn(async move {
|
tokio::spawn(async move {
|
||||||
let mut rx = rx.read().await.clone();
|
let mut rx = rx.read().await.clone();
|
||||||
@ -149,7 +344,7 @@ impl LedColorsPublisher {
|
|||||||
|
|
||||||
match Self::send_colors(colors).await {
|
match Self::send_colors(colors).await {
|
||||||
Ok(_) => {
|
Ok(_) => {
|
||||||
log::info!("colors sent. len: {}", len);
|
// log::info!("colors sent. len: {}", len);
|
||||||
}
|
}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
warn!("colors send failed: {}", err);
|
warn!("colors send failed: {}", err);
|
||||||
@ -173,8 +368,6 @@ impl LedColorsPublisher {
|
|||||||
) -> anyhow::Result<AllColorConfig> {
|
) -> anyhow::Result<AllColorConfig> {
|
||||||
let screenshot_manager = ScreenshotManager::global().await;
|
let screenshot_manager = ScreenshotManager::global().await;
|
||||||
|
|
||||||
let channels = screenshot_manager.channels.read().await;
|
|
||||||
|
|
||||||
let display_ids = configs
|
let display_ids = configs
|
||||||
.strips
|
.strips
|
||||||
.iter()
|
.iter()
|
||||||
@ -184,62 +377,83 @@ impl LedColorsPublisher {
|
|||||||
|
|
||||||
let mappers = configs.mappers.clone();
|
let mappers = configs.mappers.clone();
|
||||||
|
|
||||||
let mut local_rx_list = Vec::new();
|
|
||||||
let mut colors_configs = Vec::new();
|
let mut colors_configs = Vec::new();
|
||||||
|
|
||||||
for display_id in display_ids.clone().iter() {
|
let mut merged_screenshot_receiver = screenshot_manager.clone_merged_screenshot_rx().await;
|
||||||
let display_id = *display_id;
|
|
||||||
|
|
||||||
let channel = channels.get(&display_id);
|
let mut screenshots = HashMap::new();
|
||||||
if channel.is_none() {
|
|
||||||
anyhow::bail!("no channel for display_id: {}", display_id);
|
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
|
if screenshots.len() == display_ids.len() {
|
||||||
.strips
|
for display_id in display_ids {
|
||||||
.iter()
|
let led_strip_configs: Vec<_> = configs
|
||||||
.filter(|c| c.display_id == display_id)
|
.strips
|
||||||
.collect();
|
.iter()
|
||||||
|
.filter(|c| c.display_id == display_id)
|
||||||
|
.collect();
|
||||||
|
|
||||||
if led_strip_configs.len() == 0 {
|
if led_strip_configs.len() == 0 {
|
||||||
warn!("no led strip config for display_id: {}", display_id);
|
warn!("no led strip config for display_id: {}", display_id);
|
||||||
continue;
|
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<Vec<LedColor>> {
|
pub async fn clone_colors_receiver(&self) -> watch::Receiver<Vec<u8>> {
|
||||||
self.colors_rx.read().await.clone()
|
self.colors_rx.read().await.clone()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct AllColorConfig {
|
pub struct AllColorConfig {
|
||||||
pub sample_point_groups: Vec<SamplePointConfig>,
|
pub sample_point_groups: Vec<DisplaySamplePointGroup>,
|
||||||
pub mappers: Vec<config::SamplePointMapper>,
|
pub mappers: Vec<config::SamplePointMapper>,
|
||||||
pub screenshot_receivers: Vec<watch::Receiver<Screenshot>>,
|
// pub screenshot_receivers: Vec<watch::Receiver<Screenshot>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct DisplaySamplePointGroup {
|
||||||
|
pub display_id: u32,
|
||||||
|
pub points: Vec<Vec<LedSamplePoints>>,
|
||||||
}
|
}
|
||||||
|
@ -106,7 +106,7 @@ async fn get_one_edge_colors(
|
|||||||
if let Some(rx) = channels.get(&display_id) {
|
if let Some(rx) = channels.get(&display_id) {
|
||||||
let rx = rx.clone();
|
let rx = rx.clone();
|
||||||
let screenshot = rx.borrow().clone();
|
let screenshot = rx.borrow().clone();
|
||||||
let bytes = screenshot.bytes.read().await;
|
let bytes = screenshot.bytes.read().await.to_owned();
|
||||||
let colors =
|
let colors =
|
||||||
Screenshot::get_one_edge_colors(&sample_points, &bytes, screenshot.bytes_per_row);
|
Screenshot::get_one_edge_colors(&sample_points, &bytes, screenshot.bytes_per_row);
|
||||||
Ok(colors)
|
Ok(colors)
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
use std::iter;
|
use std::cell::RefCell;
|
||||||
|
use std::{iter, cell::Ref};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use tauri::async_runtime::RwLock;
|
use tauri::async_runtime::{RwLock, Mutex};
|
||||||
|
|
||||||
use crate::{ambient_light::LedStripConfig, led_color::LedColor};
|
use crate::{ambient_light::LedStripConfig, led_color::LedColor};
|
||||||
|
|
||||||
@ -14,9 +15,10 @@ pub struct Screenshot {
|
|||||||
pub bytes_per_row: usize,
|
pub bytes_per_row: usize,
|
||||||
pub bytes: Arc<RwLock<Vec<u8>>>,
|
pub bytes: Arc<RwLock<Vec<u8>>>,
|
||||||
pub scale_factor: f32,
|
pub scale_factor: f32,
|
||||||
pub sample_points: ScreenSamplePoints,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static SINGLE_AXIS_POINTS: usize = 5;
|
||||||
|
|
||||||
impl Screenshot {
|
impl Screenshot {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
display_id: u32,
|
display_id: u32,
|
||||||
@ -25,7 +27,6 @@ impl Screenshot {
|
|||||||
bytes_per_row: usize,
|
bytes_per_row: usize,
|
||||||
bytes: Vec<u8>,
|
bytes: Vec<u8>,
|
||||||
scale_factor: f32,
|
scale_factor: f32,
|
||||||
sample_points: ScreenSamplePoints,
|
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
display_id,
|
display_id,
|
||||||
@ -34,7 +35,6 @@ impl Screenshot {
|
|||||||
bytes_per_row,
|
bytes_per_row,
|
||||||
bytes: Arc::new(RwLock::new(bytes)),
|
bytes: Arc::new(RwLock::new(bytes)),
|
||||||
scale_factor,
|
scale_factor,
|
||||||
sample_points,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -44,10 +44,10 @@ impl Screenshot {
|
|||||||
|
|
||||||
match config.border {
|
match config.border {
|
||||||
crate::ambient_light::Border::Top => {
|
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 => {
|
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
|
points
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|groups| -> Vec<Point> {
|
.map(|groups| -> Vec<Point> {
|
||||||
@ -56,7 +56,7 @@ impl Screenshot {
|
|||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
crate::ambient_light::Border::Left => {
|
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
|
points
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|groups| -> Vec<Point> {
|
.map(|groups| -> Vec<Point> {
|
||||||
@ -65,7 +65,7 @@ impl Screenshot {
|
|||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
crate::ambient_light::Border::Right => {
|
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
|
points
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|groups| -> Vec<Point> {
|
.map(|groups| -> Vec<Point> {
|
||||||
@ -112,46 +112,6 @@ impl Screenshot {
|
|||||||
.collect()
|
.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(
|
pub fn get_one_edge_colors(
|
||||||
sample_points_of_leds: &Vec<LedSamplePoints>,
|
sample_points_of_leds: &Vec<LedSamplePoints>,
|
||||||
bitmap: &Vec<u8>,
|
bitmap: &Vec<u8>,
|
||||||
@ -176,6 +136,30 @@ impl Screenshot {
|
|||||||
colors
|
colors
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_one_edge_colors_by_cg_image(
|
||||||
|
sample_points_of_leds: &Vec<LedSamplePoints>,
|
||||||
|
bitmap: core_foundation::data::CFData,
|
||||||
|
bytes_per_row: usize,
|
||||||
|
) -> Vec<LedColor> {
|
||||||
|
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(
|
pub async fn get_colors_by_sample_points(
|
||||||
&self,
|
&self,
|
||||||
points: &Vec<LedSamplePoints>,
|
points: &Vec<LedSamplePoints>,
|
||||||
|
@ -1,13 +1,16 @@
|
|||||||
|
use std::cell::{Ref, RefCell};
|
||||||
use std::{collections::HashMap, sync::Arc};
|
use std::{collections::HashMap, sync::Arc};
|
||||||
|
|
||||||
use core_graphics::display::{
|
use core_graphics::display::{
|
||||||
kCGNullWindowID, kCGWindowImageDefault, kCGWindowListOptionOnScreenOnly, CGDisplay,
|
kCGNullWindowID, kCGWindowImageDefault, kCGWindowListOptionOnScreenOnly, CGDisplay,
|
||||||
};
|
};
|
||||||
|
use core_graphics::geometry::{CGPoint, CGRect, CGSize};
|
||||||
use paris::warn;
|
use paris::warn;
|
||||||
use tauri::async_runtime::RwLock;
|
use tauri::async_runtime::RwLock;
|
||||||
use tokio::sync::{broadcast, watch, OnceCell};
|
use tokio::sync::{broadcast, watch, OnceCell};
|
||||||
use tokio::time::{self, Duration};
|
use tokio::time::{self, Duration};
|
||||||
|
|
||||||
|
use crate::screenshot::LedSamplePoints;
|
||||||
use crate::{
|
use crate::{
|
||||||
ambient_light::{SamplePointConfig, SamplePointMapper},
|
ambient_light::{SamplePointConfig, SamplePointMapper},
|
||||||
led_color::LedColor,
|
led_color::LedColor,
|
||||||
@ -16,7 +19,6 @@ use crate::{
|
|||||||
|
|
||||||
pub fn take_screenshot(display_id: u32, scale_factor: f32) -> anyhow::Result<Screenshot> {
|
pub fn take_screenshot(display_id: u32, scale_factor: f32) -> anyhow::Result<Screenshot> {
|
||||||
log::debug!("take_screenshot");
|
log::debug!("take_screenshot");
|
||||||
// let start_at = std::time::Instant::now();
|
|
||||||
|
|
||||||
let cg_display = CGDisplay::new(display_id);
|
let cg_display = CGDisplay::new(display_id);
|
||||||
let cg_image = CGDisplay::screenshot(
|
let cg_image = CGDisplay::screenshot(
|
||||||
@ -26,7 +28,6 @@ pub fn take_screenshot(display_id: u32, scale_factor: f32) -> anyhow::Result<Scr
|
|||||||
kCGWindowImageDefault,
|
kCGWindowImageDefault,
|
||||||
)
|
)
|
||||||
.ok_or_else(|| anyhow::anyhow!("Display#{}: take screenshot failed", display_id))?;
|
.ok_or_else(|| anyhow::anyhow!("Display#{}: take screenshot failed", display_id))?;
|
||||||
// println!("take screenshot took {}ms", start_at.elapsed().as_millis());
|
|
||||||
|
|
||||||
let buffer = cg_image.data();
|
let buffer = cg_image.data();
|
||||||
let bytes_per_row = cg_image.bytes_per_row();
|
let bytes_per_row = cg_image.bytes_per_row();
|
||||||
@ -34,8 +35,7 @@ pub fn take_screenshot(display_id: u32, scale_factor: f32) -> anyhow::Result<Scr
|
|||||||
let height = cg_image.height();
|
let height = cg_image.height();
|
||||||
let width = cg_image.width();
|
let width = cg_image.width();
|
||||||
|
|
||||||
let mut bytes = vec![0u8; buffer.len() as usize];
|
let bytes = buffer.bytes().to_owned();
|
||||||
bytes.copy_from_slice(&buffer);
|
|
||||||
|
|
||||||
Ok(Screenshot::new(
|
Ok(Screenshot::new(
|
||||||
display_id,
|
display_id,
|
||||||
@ -44,18 +44,76 @@ pub fn take_screenshot(display_id: u32, scale_factor: f32) -> anyhow::Result<Scr
|
|||||||
bytes_per_row,
|
bytes_per_row,
|
||||||
bytes,
|
bytes,
|
||||||
scale_factor,
|
scale_factor,
|
||||||
ScreenSamplePoints {
|
|
||||||
top: vec![],
|
|
||||||
bottom: vec![],
|
|
||||||
left: vec![],
|
|
||||||
right: vec![],
|
|
||||||
},
|
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_display_colors(
|
||||||
|
display_id: u32,
|
||||||
|
sample_points: &Vec<Vec<LedSamplePoints>>,
|
||||||
|
) -> anyhow::Result<Vec<LedColor>> {
|
||||||
|
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::<Vec<_>>()
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
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 struct ScreenshotManager {
|
||||||
pub channels: Arc<RwLock<HashMap<u32, watch::Receiver<Screenshot>>>>,
|
pub channels: Arc<RwLock<HashMap<u32, watch::Receiver<Screenshot>>>>,
|
||||||
merged_screenshot_rx: Arc<RwLock<broadcast::Receiver<Screenshot>>>,
|
|
||||||
merged_screenshot_tx: Arc<RwLock<broadcast::Sender<Screenshot>>>,
|
merged_screenshot_tx: Arc<RwLock<broadcast::Sender<Screenshot>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -66,10 +124,9 @@ impl ScreenshotManager {
|
|||||||
SCREENSHOT_MANAGER
|
SCREENSHOT_MANAGER
|
||||||
.get_or_init(|| async {
|
.get_or_init(|| async {
|
||||||
let channels = Arc::new(RwLock::new(HashMap::new()));
|
let channels = Arc::new(RwLock::new(HashMap::new()));
|
||||||
let (merged_screenshot_tx, merged_screenshot_rx) = broadcast::channel(2);
|
let (merged_screenshot_tx, _) = broadcast::channel::<Screenshot>(2);
|
||||||
Self {
|
Self {
|
||||||
channels,
|
channels,
|
||||||
merged_screenshot_rx: Arc::new(RwLock::new(merged_screenshot_rx)),
|
|
||||||
merged_screenshot_tx: Arc::new(RwLock::new(merged_screenshot_tx)),
|
merged_screenshot_tx: Arc::new(RwLock::new(merged_screenshot_tx)),
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@ -94,7 +151,8 @@ impl ScreenshotManager {
|
|||||||
warn!("take_screenshot_loop: {}", screenshot.err().unwrap());
|
warn!("take_screenshot_loop: {}", screenshot.err().unwrap());
|
||||||
return;
|
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 = screenshot.unwrap();
|
||||||
let (screenshot_tx, screenshot_rx) = watch::channel(screenshot);
|
let (screenshot_tx, screenshot_rx) = watch::channel(screenshot);
|
||||||
@ -107,7 +165,7 @@ impl ScreenshotManager {
|
|||||||
let merged_screenshot_tx = merged_screenshot_tx.read().await.clone();
|
let merged_screenshot_tx = merged_screenshot_tx.read().await.clone();
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
// interval.tick().await;
|
start = tokio::time::Instant::now();
|
||||||
Self::take_screenshot_loop(
|
Self::take_screenshot_loop(
|
||||||
display_id,
|
display_id,
|
||||||
scale_factor,
|
scale_factor,
|
||||||
@ -115,7 +173,8 @@ impl ScreenshotManager {
|
|||||||
&merged_screenshot_tx,
|
&merged_screenshot_tx,
|
||||||
)
|
)
|
||||||
.await;
|
.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);
|
let screenshot = take_screenshot(display_id, scale_factor);
|
||||||
if let Ok(screenshot) = screenshot {
|
if let Ok(screenshot) = screenshot {
|
||||||
screenshot_tx.send(screenshot.clone()).unwrap();
|
match merged_screenshot_tx.send(screenshot.clone()) {
|
||||||
merged_screenshot_tx.send(screenshot).unwrap();
|
Ok(_) => {}
|
||||||
log::debug!("take_screenshot_loop: send success. display#{}", display_id)
|
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 {
|
} else {
|
||||||
warn!("take_screenshot_loop: {}", screenshot.err().unwrap());
|
warn!("take_screenshot_loop: {}", screenshot.err().unwrap());
|
||||||
}
|
}
|
||||||
@ -155,10 +219,7 @@ impl ScreenshotManager {
|
|||||||
all_colors
|
all_colors
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn get_sorted_colors(
|
pub fn get_sorted_colors(colors: &Vec<u8>, mappers: &Vec<SamplePointMapper>) -> Vec<u8> {
|
||||||
colors: &Vec<LedColor>,
|
|
||||||
mappers: &Vec<SamplePointMapper>,
|
|
||||||
) -> Vec<u8> {
|
|
||||||
let total_leds = mappers
|
let total_leds = mappers
|
||||||
.iter()
|
.iter()
|
||||||
.map(|mapper| usize::max(mapper.start, mapper.end))
|
.map(|mapper| usize::max(mapper.start, mapper.end))
|
||||||
@ -178,33 +239,29 @@ impl ScreenshotManager {
|
|||||||
return;
|
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!(
|
warn!(
|
||||||
"get_sorted_colors: color_index out of range. color_index: {}, strip len: {}, colors.len(): {}",
|
"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),
|
group.start.abs_diff(group.end),
|
||||||
colors.len()
|
colors.len() / 3
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if group.end > group.start {
|
if group.end > group.start {
|
||||||
for i in group.start..group.end {
|
for i in group.start..group.end {
|
||||||
let rgb = colors[color_index].get_rgb();
|
global_colors[i * 3] = colors[color_index +0];
|
||||||
color_index += 1;
|
global_colors[i * 3 + 1] = colors[color_index +1];
|
||||||
|
global_colors[i * 3 + 2] = colors[color_index +2];
|
||||||
global_colors[i * 3] = rgb[0];
|
color_index += 3;
|
||||||
global_colors[i * 3 + 1] = rgb[1];
|
|
||||||
global_colors[i * 3 + 2] = rgb[2];
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
for i in (group.end..group.start).rev() {
|
for i in (group.end..group.start).rev() {
|
||||||
let rgb = colors[color_index].get_rgb();
|
global_colors[i * 3] = colors[color_index +0];
|
||||||
color_index += 1;
|
global_colors[i * 3 + 1] = colors[color_index +1];
|
||||||
|
global_colors[i * 3 + 2] = colors[color_index +2];
|
||||||
global_colors[i * 3] = rgb[0];
|
color_index += 3;
|
||||||
global_colors[i * 3 + 1] = rgb[1];
|
|
||||||
global_colors[i * 3 + 2] = rgb[2];
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -44,7 +44,7 @@ function App() {
|
|||||||
|
|
||||||
// listen to led_colors_changed event
|
// listen to led_colors_changed event
|
||||||
createEffect(() => {
|
createEffect(() => {
|
||||||
const unlisten = listen<Array<string>>('led_colors_changed', (event) => {
|
const unlisten = listen<Uint8ClampedArray>('led_colors_changed', (event) => {
|
||||||
const colors = event.payload;
|
const colors = event.payload;
|
||||||
|
|
||||||
setLedStripStore({
|
setLedStripStore({
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import { invoke } from '@tauri-apps/api';
|
import { invoke } from '@tauri-apps/api';
|
||||||
import { listen } from '@tauri-apps/api/event';
|
|
||||||
import {
|
import {
|
||||||
Component,
|
Component,
|
||||||
createEffect,
|
createEffect,
|
||||||
@ -8,7 +7,6 @@ import {
|
|||||||
createSignal,
|
createSignal,
|
||||||
For,
|
For,
|
||||||
JSX,
|
JSX,
|
||||||
onCleanup,
|
|
||||||
splitProps,
|
splitProps,
|
||||||
useContext,
|
useContext,
|
||||||
} from 'solid-js';
|
} from 'solid-js';
|
||||||
@ -77,9 +75,15 @@ export const LedStripPart: Component<LedStripPartProps> = (props) => {
|
|||||||
return;
|
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);
|
setColors(colors);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -115,12 +115,21 @@ const SorterItem: Component<{ strip: LedStripConfig; mapper: LedStripPixelMapper
|
|||||||
// update fullLeds
|
// update fullLeds
|
||||||
createEffect(() => {
|
createEffect(() => {
|
||||||
const fullLeds = new Array(ledStripStore.totalLedCount).fill(null);
|
const fullLeds = new Array(ledStripStore.totalLedCount).fill(null);
|
||||||
|
const colors = ledStripStore.colors;
|
||||||
|
|
||||||
const { start, end, pos } = props.mapper;
|
const { start, end, pos } = props.mapper;
|
||||||
const isForward = start < end;
|
const isForward = start < end;
|
||||||
const step = isForward ? 1 : -1;
|
const step = isForward ? 1 : -1;
|
||||||
for (let i = start, j = pos; i !== end; i += step, j++) {
|
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);
|
setFullLeds(fullLeds);
|
||||||
|
@ -4,7 +4,7 @@ import { LedStripConfig, LedStripPixelMapper } from '../models/led-strip-config'
|
|||||||
export const [ledStripStore, setLedStripStore] = createStore({
|
export const [ledStripStore, setLedStripStore] = createStore({
|
||||||
strips: new Array<LedStripConfig>(),
|
strips: new Array<LedStripConfig>(),
|
||||||
mappers: new Array<LedStripPixelMapper>(),
|
mappers: new Array<LedStripPixelMapper>(),
|
||||||
colors: new Array<string>(),
|
colors: new Uint8ClampedArray(),
|
||||||
sortedColors: new Uint8ClampedArray(),
|
sortedColors: new Uint8ClampedArray(),
|
||||||
get totalLedCount() {
|
get totalLedCount() {
|
||||||
return Math.max(0, ...ledStripStore.mappers.map((m) => Math.max(m.start, m.end)));
|
return Math.max(0, ...ledStripStore.mappers.map((m) => Math.max(m.start, m.end)));
|
||||||
|
Loading…
Reference in New Issue
Block a user