feat: 改进配置方式,重写逻辑。
This commit is contained in:
parent
7e1c4dd245
commit
479fdba9f6
1
src-tauri/Cargo.lock
generated
1
src-tauri/Cargo.lock
generated
@ -573,6 +573,7 @@ dependencies = [
|
|||||||
"base64",
|
"base64",
|
||||||
"bmp",
|
"bmp",
|
||||||
"color_space",
|
"color_space",
|
||||||
|
"futures",
|
||||||
"hex",
|
"hex",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"paris",
|
"paris",
|
||||||
|
@ -31,6 +31,7 @@ hex = "0.4.3"
|
|||||||
rumqttc = "0.17.0"
|
rumqttc = "0.17.0"
|
||||||
time = { version = "0.3.17", features = ["formatting"] }
|
time = { version = "0.3.17", features = ["formatting"] }
|
||||||
color_space = "0.5.3"
|
color_space = "0.5.3"
|
||||||
|
futures = "0.3.25"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
# by default Tauri runs in production mode
|
# by default Tauri runs in production mode
|
||||||
|
@ -1,16 +1,23 @@
|
|||||||
|
use futures::{future::join_all, stream::FuturesUnordered, StreamExt};
|
||||||
use once_cell::sync::OnceCell;
|
use once_cell::sync::OnceCell;
|
||||||
use paris::info;
|
use paris::info;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::{sync::Arc, time::Duration};
|
use serde_json::value::Index;
|
||||||
|
use std::{collections::HashMap, iter::Map, sync::Arc, thread, time::Duration};
|
||||||
use tauri::async_runtime::RwLock;
|
use tauri::async_runtime::RwLock;
|
||||||
use tokio::{
|
use tokio::{
|
||||||
|
join,
|
||||||
|
sync::mpsc,
|
||||||
task,
|
task,
|
||||||
time::{sleep, Instant},
|
time::{sleep, Instant},
|
||||||
};
|
};
|
||||||
use tracing::warn;
|
use tracing::warn;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
picker::{led_color::LedColor, manager::Picker},
|
picker::{
|
||||||
|
config::DisplayConfig, display_picker::DisplayPicker, led_color::LedColor, manager::Picker,
|
||||||
|
screenshot::Screenshot,
|
||||||
|
},
|
||||||
rpc,
|
rpc,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -84,51 +91,100 @@ impl CoreManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub async fn play_follow(&self) -> anyhow::Result<()> {
|
pub async fn play_follow(&self) -> anyhow::Result<()> {
|
||||||
{
|
|
||||||
let lock = self.ambient_light_mode.read().await;
|
let lock = self.ambient_light_mode.read().await;
|
||||||
|
let mut futs = vec![];
|
||||||
if let AmbientLightMode::Follow = *lock {
|
if let AmbientLightMode::Follow = *lock {
|
||||||
Picker::global().refresh_displays().await?;
|
drop(lock);
|
||||||
|
let configs = Picker::global().display_configs.lock().await;
|
||||||
|
|
||||||
|
let (tx, mut rx) = mpsc::channel(10);
|
||||||
|
|
||||||
|
for config in configs.to_owned() {
|
||||||
|
let tx = tx.clone();
|
||||||
|
let fut = tokio::spawn(async move {
|
||||||
|
match Self::follow_display_by_config(config, tx).await {
|
||||||
|
Ok(_) => {}
|
||||||
|
Err(error) => {
|
||||||
|
warn!("following failed. {}", error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
futs.push(fut);
|
||||||
|
}
|
||||||
|
|
||||||
|
let configs = configs.clone();
|
||||||
|
|
||||||
|
tokio::spawn(async move {
|
||||||
|
let mut global_colors = HashMap::new();
|
||||||
|
while let Some(screenshot) = rx.recv().await {
|
||||||
|
let start_at = Instant::now();
|
||||||
|
match screenshot.get_top_colors().await {
|
||||||
|
Ok(colors) => {
|
||||||
|
let start = screenshot.get_top_of_led_strip_range().min().unwrap_or(0);
|
||||||
|
|
||||||
|
let colors_len = colors.len();
|
||||||
|
for (index, color) in colors.into_iter().enumerate() {
|
||||||
|
global_colors.insert(index + start, color);
|
||||||
|
}
|
||||||
|
|
||||||
|
if global_colors.len() == 60 {
|
||||||
|
let mut colors = vec![];
|
||||||
|
for index in 0..global_colors.len() {
|
||||||
|
colors.push(*global_colors.get(&index).unwrap());
|
||||||
|
}
|
||||||
|
global_colors = HashMap::new();
|
||||||
|
match rpc::manager::Manager::global()
|
||||||
|
.publish_led_colors(&colors)
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
Ok(_) => {
|
||||||
|
info!("publish successful",);
|
||||||
|
}
|
||||||
|
Err(error) => {
|
||||||
|
warn!("publish led colors failed. {}", error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(_) => {}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
join_all(futs).await;
|
||||||
} else {
|
} else {
|
||||||
|
drop(lock);
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn follow_display_by_config(
|
||||||
|
config: DisplayConfig,
|
||||||
|
tx: mpsc::Sender<Screenshot>,
|
||||||
|
) -> anyhow::Result<()> {
|
||||||
|
let mut picker = DisplayPicker::from_config(config)?;
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
let start = Instant::now();
|
let start = Instant::now();
|
||||||
let next_tick = start + Duration::from_millis(16);
|
let next_tick = start + Duration::from_millis(16);
|
||||||
info!("Following");
|
let lock = Self::global().ambient_light_mode.read().await;
|
||||||
let lock = self.ambient_light_mode.read().await;
|
|
||||||
if let AmbientLightMode::Follow = *lock {
|
if let AmbientLightMode::Follow = *lock {
|
||||||
task::spawn(async {
|
drop(lock);
|
||||||
let start = Instant::now();
|
let screenshot = picker.take_screenshot()?;
|
||||||
match Self::follow_once().await {
|
// info!("Take Screenshot Spend: {:?}", start.elapsed());
|
||||||
Ok(_) => {}
|
tx.send(screenshot).await;
|
||||||
Err(error) => {
|
|
||||||
warn!("take screenshots failed. {}", error);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
println!(
|
|
||||||
"Time elapsed in expensive_function() is: {:?}",
|
|
||||||
start.elapsed()
|
|
||||||
);
|
|
||||||
});
|
|
||||||
} else {
|
} else {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
tokio::time::sleep_until(next_tick).await;
|
tokio::time::sleep_until(next_tick).await;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
// // Picker::global().take_screenshots_for_all().await?;
|
||||||
}
|
// // let colors = Picker::global().get_led_strip_colors().await?;
|
||||||
|
|
||||||
async fn follow_once() -> anyhow::Result<()> {
|
// // let colors = colors.into_iter().rev().collect();
|
||||||
Picker::global().take_screenshots_for_all().await?;
|
|
||||||
let colors = Picker::global().get_led_strip_colors().await?;
|
|
||||||
|
|
||||||
let colors = colors.into_iter().rev().collect();
|
|
||||||
rpc::manager::Manager::global()
|
|
||||||
.publish_led_colors(&colors)
|
|
||||||
.await?;
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,12 +7,12 @@ mod core;
|
|||||||
mod picker;
|
mod picker;
|
||||||
mod rpc;
|
mod rpc;
|
||||||
|
|
||||||
use crate::core::CoreManager;
|
|
||||||
use crate::core::AmbientLightMode;
|
use crate::core::AmbientLightMode;
|
||||||
|
use crate::core::CoreManager;
|
||||||
use paris::*;
|
use paris::*;
|
||||||
use picker::led_color::LedColor;
|
use picker::led_color::LedColor;
|
||||||
use picker::manager::Picker;
|
use picker::manager::Picker;
|
||||||
use std::time::Instant;
|
use std::vec;
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
async fn refresh_displays() {
|
async fn refresh_displays() {
|
||||||
@ -26,24 +26,22 @@ async fn refresh_displays() {
|
|||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
async fn take_snapshot() -> Vec<String> {
|
async fn take_snapshot() -> Vec<String> {
|
||||||
let start = Instant::now();
|
|
||||||
let manager = Picker::global();
|
let manager = Picker::global();
|
||||||
|
|
||||||
match manager.take_screenshots_for_all().await {
|
let start = time::Instant::now();
|
||||||
Ok(screenshots) => {
|
let base64_bitmap_list = match manager.list_displays().await {
|
||||||
info!("screenshots len: {}", screenshots.len());
|
Ok(base64_bitmap_list) => {
|
||||||
let mut futures = Vec::new();
|
info!("screenshots len: {}", base64_bitmap_list.len());
|
||||||
for screenshot in screenshots {
|
base64_bitmap_list
|
||||||
let future = screenshot.to_webp_base64().await;
|
|
||||||
futures.push(future);
|
|
||||||
}
|
|
||||||
futures
|
|
||||||
}
|
}
|
||||||
Err(error) => {
|
Err(error) => {
|
||||||
error!("can not take screenshots for all. {}", error);
|
error!("can not take screenshots for all. {}", error);
|
||||||
|
|
||||||
vec![]
|
vec![]
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
info!("截图花费 {} s", start.elapsed().as_seconds_f32());
|
||||||
|
base64_bitmap_list
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
|
58
src-tauri/src/picker/config.rs
Normal file
58
src-tauri/src/picker/config.rs
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
#[derive(Clone, Copy)]
|
||||||
|
pub struct LedStripConfig {
|
||||||
|
pub index: usize,
|
||||||
|
pub global_start_position: usize,
|
||||||
|
pub global_end_position: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
pub struct DisplayConfig {
|
||||||
|
pub index_of_display: usize,
|
||||||
|
pub display_width: usize,
|
||||||
|
pub display_height: usize,
|
||||||
|
pub top_led_strip: LedStripConfig,
|
||||||
|
pub bottom_led_strip: LedStripConfig,
|
||||||
|
pub left_led_strip: LedStripConfig,
|
||||||
|
pub right_led_strip: LedStripConfig,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
pub enum LedFlowX {
|
||||||
|
LR, // from left to right
|
||||||
|
RL, // from right to left
|
||||||
|
}
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
pub enum LedFlowY {
|
||||||
|
TB, // from top to bottom
|
||||||
|
BT, // from bottom to top
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DisplayConfig {
|
||||||
|
pub fn default(index_of_display: usize, display_width: usize, display_height: usize) -> Self {
|
||||||
|
Self {
|
||||||
|
index_of_display,
|
||||||
|
display_width,
|
||||||
|
display_height,
|
||||||
|
top_led_strip: LedStripConfig {
|
||||||
|
index: 0,
|
||||||
|
global_start_position: 0,
|
||||||
|
global_end_position: 0,
|
||||||
|
},
|
||||||
|
bottom_led_strip: LedStripConfig {
|
||||||
|
index: 0,
|
||||||
|
global_start_position: 0,
|
||||||
|
global_end_position: 0,
|
||||||
|
},
|
||||||
|
left_led_strip: LedStripConfig {
|
||||||
|
index: 0,
|
||||||
|
global_start_position: 0,
|
||||||
|
global_end_position: 0,
|
||||||
|
},
|
||||||
|
right_led_strip: LedStripConfig {
|
||||||
|
index: 0,
|
||||||
|
global_start_position: 0,
|
||||||
|
global_end_position: 0,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
51
src-tauri/src/picker/display_picker.rs
Normal file
51
src-tauri/src/picker/display_picker.rs
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
use paris::info;
|
||||||
|
use scrap::{Capturer, Display};
|
||||||
|
|
||||||
|
use super::{config::DisplayConfig, screen::Screen, screenshot::Screenshot};
|
||||||
|
|
||||||
|
pub struct DisplayPicker {
|
||||||
|
pub screen: Screen,
|
||||||
|
pub config: DisplayConfig,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DisplayPicker {
|
||||||
|
pub fn new(screen: Screen, config: DisplayConfig) -> Self {
|
||||||
|
Self { screen, config }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from_config(config: DisplayConfig) -> anyhow::Result<Self> {
|
||||||
|
let displays = Display::all()
|
||||||
|
.map_err(|error| anyhow::anyhow!("Can not get all of displays. {}", error))?;
|
||||||
|
let display = displays
|
||||||
|
.into_iter()
|
||||||
|
.skip(config.index_of_display)
|
||||||
|
.next();
|
||||||
|
|
||||||
|
match display {
|
||||||
|
Some(display) => {
|
||||||
|
let height = display.height();
|
||||||
|
let width = display.width();
|
||||||
|
info!("dw: {}, cw: {}", width, config.display_height);
|
||||||
|
assert_eq!(width, config.display_width);
|
||||||
|
let capturer = Capturer::new(display)?;
|
||||||
|
let screen = Screen::new(capturer, width, height);
|
||||||
|
|
||||||
|
Ok(Self { screen, config })
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
anyhow::bail!("Index out of displays range.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn take_screenshot(&mut self) -> anyhow::Result<Screenshot> {
|
||||||
|
let bitmap = self
|
||||||
|
.screen
|
||||||
|
.take()
|
||||||
|
.map_err(|error| anyhow::anyhow!("take screenshot for display failed. {}", error))?;
|
||||||
|
|
||||||
|
// info!("bitmap size {}", bitmap.len());
|
||||||
|
let screenshot = Screenshot::new(bitmap, self.config);
|
||||||
|
Ok(screenshot)
|
||||||
|
}
|
||||||
|
}
|
@ -1,16 +1,24 @@
|
|||||||
|
use futures::{stream::FuturesUnordered, StreamExt};
|
||||||
use once_cell::sync::OnceCell;
|
use once_cell::sync::OnceCell;
|
||||||
use paris::*;
|
use paris::info;
|
||||||
use scrap::{Capturer, Display};
|
use scrap::Display;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use tokio::sync::Mutex;
|
use tokio::{sync::Mutex, task};
|
||||||
|
|
||||||
use crate::picker::screen::Screen;
|
use crate::picker::{
|
||||||
|
config::{LedFlowX, LedFlowY, LedStripConfig},
|
||||||
|
screen::Screen,
|
||||||
|
};
|
||||||
|
|
||||||
use super::{led_color::LedColor, screenshot::Screenshot};
|
use super::{
|
||||||
|
config::DisplayConfig, display_picker::DisplayPicker, led_color::LedColor,
|
||||||
|
screenshot::Screenshot,
|
||||||
|
};
|
||||||
|
|
||||||
pub struct Picker {
|
pub struct Picker {
|
||||||
pub screens: Arc<Mutex<Vec<Screen>>>,
|
pub screens: Arc<Mutex<Vec<Screen>>>,
|
||||||
pub screenshots: Arc<Mutex<Vec<Screenshot>>>,
|
pub screenshots: Arc<Mutex<Vec<Screenshot>>>,
|
||||||
|
pub display_configs: Arc<Mutex<Vec<DisplayConfig>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Picker {
|
impl Picker {
|
||||||
@ -20,46 +28,144 @@ impl Picker {
|
|||||||
SCREEN_COLOR_PICKER.get_or_init(|| Picker {
|
SCREEN_COLOR_PICKER.get_or_init(|| Picker {
|
||||||
screens: Arc::new(Mutex::new(vec![])),
|
screens: Arc::new(Mutex::new(vec![])),
|
||||||
screenshots: Arc::new(Mutex::new(vec![])),
|
screenshots: Arc::new(Mutex::new(vec![])),
|
||||||
|
display_configs: Arc::new(Mutex::new(vec![
|
||||||
|
DisplayConfig {
|
||||||
|
index_of_display: 1,
|
||||||
|
display_width: 1920,
|
||||||
|
display_height: 1200,
|
||||||
|
top_led_strip: LedStripConfig {
|
||||||
|
index: 1,
|
||||||
|
global_start_position: 32,
|
||||||
|
global_end_position: 60,
|
||||||
|
},
|
||||||
|
bottom_led_strip: LedStripConfig {
|
||||||
|
index: 0,
|
||||||
|
global_start_position: 0,
|
||||||
|
global_end_position: 0,
|
||||||
|
},
|
||||||
|
left_led_strip: LedStripConfig {
|
||||||
|
index: 0,
|
||||||
|
global_start_position: 0,
|
||||||
|
global_end_position: 0,
|
||||||
|
},
|
||||||
|
right_led_strip: LedStripConfig {
|
||||||
|
index: 0,
|
||||||
|
global_start_position: 0,
|
||||||
|
global_end_position: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
DisplayConfig {
|
||||||
|
index_of_display: 0,
|
||||||
|
display_width: 3008,
|
||||||
|
display_height: 1692,
|
||||||
|
top_led_strip: LedStripConfig {
|
||||||
|
index: 0,
|
||||||
|
global_start_position: 0,
|
||||||
|
global_end_position: 32,
|
||||||
|
},
|
||||||
|
bottom_led_strip: LedStripConfig {
|
||||||
|
index: 0,
|
||||||
|
global_start_position: 0,
|
||||||
|
global_end_position: 0,
|
||||||
|
},
|
||||||
|
left_led_strip: LedStripConfig {
|
||||||
|
index: 0,
|
||||||
|
global_start_position: 0,
|
||||||
|
global_end_position: 0,
|
||||||
|
},
|
||||||
|
right_led_strip: LedStripConfig {
|
||||||
|
index: 0,
|
||||||
|
global_start_position: 0,
|
||||||
|
global_end_position: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
])),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn refresh_displays(&self) -> anyhow::Result<()> {
|
pub async fn list_displays(&self) -> anyhow::Result<Vec<String>> {
|
||||||
|
let mut configs = self.display_configs.lock().await;
|
||||||
|
let screenshots = self.screenshots.lock().await;
|
||||||
|
|
||||||
let displays = Display::all()
|
let displays = Display::all()
|
||||||
.map_err(|error| anyhow::anyhow!("Can not get all of displays. {}", error))?;
|
.map_err(|error| anyhow::anyhow!("Can not get all of displays. {}", error))?;
|
||||||
let mut screens = self.screens.lock().await;
|
|
||||||
let mut screenshots = self.screenshots.lock().await;
|
configs.clear();
|
||||||
screens.clear();
|
let mut futs = FuturesUnordered::new();
|
||||||
info!("number of displays: {}", displays.len());
|
|
||||||
for display in displays {
|
for (index, display) in displays.iter().enumerate() {
|
||||||
let height = display.height();
|
let height = display.height();
|
||||||
let width = display.width();
|
let width = display.width();
|
||||||
match Capturer::new(display) {
|
let config = DisplayConfig::default(index, width, height);
|
||||||
Ok(capturer) => screens.push(Screen::new(capturer, width, height)),
|
configs.push(config);
|
||||||
Err(error) => screens.push(Screen::new_failed(
|
|
||||||
anyhow::anyhow!("{}", error),
|
|
||||||
width,
|
|
||||||
height,
|
|
||||||
)),
|
|
||||||
};
|
|
||||||
screenshots.push(Screenshot::new(width, height));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
screens.reverse();
|
for (index, display) in displays.iter().enumerate() {
|
||||||
screenshots.reverse();
|
let height = display.height();
|
||||||
screenshots[0].set_number_of_leds(22, 0);
|
let width = display.width();
|
||||||
screenshots[1].set_number_of_leds(38, 0);
|
let config = configs[index];
|
||||||
|
futs.push(async move {
|
||||||
|
let join = task::spawn(Self::preview_display_by_config(config));
|
||||||
|
join.await?
|
||||||
|
});
|
||||||
|
}
|
||||||
|
let mut bitmap_string_list = vec![];
|
||||||
|
while let Some(bitmap_string) = futs.next().await {
|
||||||
|
match bitmap_string {
|
||||||
|
Ok(bitmap_string) => {
|
||||||
|
bitmap_string_list.push(bitmap_string);
|
||||||
|
}
|
||||||
|
Err(error) => {
|
||||||
|
anyhow::bail!("can not convert to base64 image. {}", error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(bitmap_string_list)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn preview_display_by_config(config: DisplayConfig) -> anyhow::Result<String> {
|
||||||
|
let start = time::Instant::now();
|
||||||
|
let mut picker = DisplayPicker::from_config(config)?;
|
||||||
|
let screenshot = picker.take_screenshot()?;
|
||||||
|
info!("Take Screenshot Spend: {}", start.elapsed());
|
||||||
|
|
||||||
|
anyhow::Ok(screenshot.to_webp_base64().await)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn refresh_displays(&self) -> anyhow::Result<()> {
|
||||||
|
// let displays = Display::all()
|
||||||
|
// .map_err(|error| anyhow::anyhow!("Can not get all of displays. {}", error))?;
|
||||||
|
// let mut screens = self.screens.lock().await;
|
||||||
|
// let mut screenshots = self.screenshots.lock().await;
|
||||||
|
// screens.clear();
|
||||||
|
// info!("number of displays: {}", displays.len());
|
||||||
|
// for display in displays {
|
||||||
|
// let height = display.height();
|
||||||
|
// let width = display.width();
|
||||||
|
// match Capturer::new(display) {
|
||||||
|
// Ok(capturer) => screens.push(Screen::new(capturer, width, height)),
|
||||||
|
// Err(error) => screens.push(Screen::new_failed(
|
||||||
|
// anyhow::anyhow!("{}", error),
|
||||||
|
// width,
|
||||||
|
// height,
|
||||||
|
// )),
|
||||||
|
// };
|
||||||
|
// screenshots.push(Screenshot::new(width, height));
|
||||||
|
// }
|
||||||
|
|
||||||
|
// screens.reverse();
|
||||||
|
// screenshots.reverse();
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn take_screenshots_for_all(&self) -> anyhow::Result<Vec<Screenshot>> {
|
pub async fn take_screenshots_for_all(&self) -> anyhow::Result<Vec<Screenshot>> {
|
||||||
let mut screens = self.screens.lock().await;
|
let mut screens = self.screens.lock().await;
|
||||||
let mut screenshots = self.screenshots.lock().await;
|
let screenshots = self.screenshots.lock().await;
|
||||||
for (index, screen) in screens.iter_mut().enumerate() {
|
for (index, screen) in screens.iter_mut().enumerate() {
|
||||||
let bitmap = screen.take().map_err(|error| {
|
let bitmap = screen.take().map_err(|error| {
|
||||||
anyhow::anyhow!("take screenshot for display failed. {}", error)
|
anyhow::anyhow!("take screenshot for display failed. {}", error)
|
||||||
})?;
|
})?;
|
||||||
screenshots[index].set_bitmap(bitmap).await
|
|
||||||
}
|
}
|
||||||
Ok(screenshots.to_vec())
|
Ok(screenshots.to_vec())
|
||||||
}
|
}
|
||||||
|
@ -2,3 +2,5 @@ pub mod led_color;
|
|||||||
pub mod screen;
|
pub mod screen;
|
||||||
pub mod manager;
|
pub mod manager;
|
||||||
pub mod screenshot;
|
pub mod screenshot;
|
||||||
|
pub mod display_picker;
|
||||||
|
pub mod config;
|
@ -1,5 +1,5 @@
|
|||||||
|
|
||||||
use scrap::Capturer;
|
use scrap::Capturer;
|
||||||
|
use std::{io::ErrorKind::WouldBlock, time::Duration, thread};
|
||||||
|
|
||||||
pub struct Screen {
|
pub struct Screen {
|
||||||
capturer: Option<Capturer>,
|
capturer: Option<Capturer>,
|
||||||
@ -29,12 +29,21 @@ impl Screen {
|
|||||||
|
|
||||||
pub fn take(&mut self) -> anyhow::Result<Vec<u8>> {
|
pub fn take(&mut self) -> anyhow::Result<Vec<u8>> {
|
||||||
match self.capturer.as_mut() {
|
match self.capturer.as_mut() {
|
||||||
Some(capturer) => {
|
Some(capturer) => loop {
|
||||||
let buffer = capturer
|
match capturer.frame() {
|
||||||
.frame()
|
Ok(buffer) => {
|
||||||
.map_err(|error| anyhow::anyhow!("failed to frame of display. {}", error))?;
|
return anyhow::Ok(buffer.to_vec());
|
||||||
anyhow::Ok(buffer.to_vec())
|
|
||||||
}
|
}
|
||||||
|
Err(error) => {
|
||||||
|
if error.kind() == WouldBlock {
|
||||||
|
thread::sleep(Duration::from_millis(16));
|
||||||
|
continue;
|
||||||
|
} else {
|
||||||
|
anyhow::bail!("failed to frame of display. {}", error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
None => anyhow::bail!("Do not initialized"),
|
None => anyhow::bail!("Do not initialized"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,69 +1,61 @@
|
|||||||
use color_space::{Hsv, Rgb};
|
|
||||||
use paris::info;
|
|
||||||
use std::sync::Arc;
|
|
||||||
use tokio::sync::Mutex;
|
|
||||||
|
|
||||||
use super::led_color::LedColor;
|
use std::ops::Range;
|
||||||
|
|
||||||
|
use color_space::{Hsv, Rgb};
|
||||||
|
|
||||||
|
use super::{
|
||||||
|
config::{DisplayConfig, LedStripConfig},
|
||||||
|
led_color::LedColor,
|
||||||
|
};
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct Screenshot {
|
pub struct Screenshot {
|
||||||
bitmap: Arc<Mutex<Option<Vec<u8>>>>,
|
bitmap: Vec<u8>,
|
||||||
width: usize,
|
config: DisplayConfig,
|
||||||
height: usize,
|
|
||||||
led_number_of_x: usize,
|
|
||||||
led_number_of_y: usize,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Screenshot {
|
impl Screenshot {
|
||||||
pub fn new(width: usize, height: usize) -> Self {
|
pub fn new(bitmap: Vec<u8>, config: DisplayConfig) -> Self {
|
||||||
Self {
|
Self { bitmap, config }
|
||||||
bitmap: Arc::new(Mutex::new(None)),
|
|
||||||
led_number_of_x: 0,
|
|
||||||
led_number_of_y: 0,
|
|
||||||
width,
|
|
||||||
height,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_size(&self) -> (usize, usize) {
|
|
||||||
(self.width, self.height)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_number_of_leds(&self) -> (usize, usize) {
|
|
||||||
(self.led_number_of_x, self.led_number_of_y)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_number_of_leds(&mut self, led_number_of_x: usize, led_number_of_y: usize) {
|
|
||||||
self.led_number_of_x = led_number_of_x;
|
|
||||||
self.led_number_of_y = led_number_of_y;
|
|
||||||
}
|
|
||||||
pub async fn get_top_colors(&self) -> anyhow::Result<Vec<LedColor>> {
|
pub async fn get_top_colors(&self) -> anyhow::Result<Vec<LedColor>> {
|
||||||
self.get_x_colors(XPosition::Top).await
|
self.get_x_colors(XPosition::Top, self.config.top_led_strip)
|
||||||
|
.await
|
||||||
}
|
}
|
||||||
pub async fn get_bottom_colors(&self) -> anyhow::Result<Vec<LedColor>> {
|
pub async fn get_bottom_colors(&self) -> anyhow::Result<Vec<LedColor>> {
|
||||||
self.get_x_colors(XPosition::Bottom).await
|
self.get_x_colors(XPosition::Bottom, self.config.bottom_led_strip)
|
||||||
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn get_x_colors(&self, position: XPosition) -> anyhow::Result<Vec<LedColor>> {
|
pub fn get_top_of_led_strip_range(&self) -> Range<usize> {
|
||||||
if self.led_number_of_x == 0 {
|
self.config.top_led_strip.global_start_position
|
||||||
return Ok(vec![]);
|
..self.config.top_led_strip.global_end_position
|
||||||
}
|
}
|
||||||
|
|
||||||
let bitmap = self.bitmap.lock().await;
|
async fn get_x_colors(
|
||||||
match bitmap.as_ref() {
|
&self,
|
||||||
Some(bitmap) => {
|
position: XPosition,
|
||||||
let cell_size_x = self.width / self.led_number_of_x;
|
strip_config: LedStripConfig,
|
||||||
let cell_size_y = self.height / 8;
|
) -> anyhow::Result<Vec<LedColor>> {
|
||||||
|
let bitmap = &self.bitmap;
|
||||||
|
let number_of_leds = strip_config
|
||||||
|
.global_start_position
|
||||||
|
.abs_diff(strip_config.global_end_position);
|
||||||
|
let cell_size_x = self.config.display_width / number_of_leds;
|
||||||
|
let cell_size_y = self.config.display_height / 8;
|
||||||
let cell_size = cell_size_x * cell_size_y;
|
let cell_size = cell_size_x * cell_size_y;
|
||||||
let y_range = match position {
|
let y_range = match position {
|
||||||
XPosition::Top => 20..cell_size_y + 20,
|
XPosition::Top => 20..cell_size_y + 20,
|
||||||
XPosition::Bottom => self.height - 20 - cell_size_y..self.height - 20,
|
XPosition::Bottom => {
|
||||||
|
self.config.display_height - 20 - cell_size_y..self.config.display_height - 20
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut colors = Vec::new();
|
let mut colors = Vec::new();
|
||||||
let stride = bitmap.len() / self.height;
|
let stride = bitmap.len() / self.config.display_height;
|
||||||
|
|
||||||
for pos in 0..self.led_number_of_x {
|
for pos in strip_config.global_start_position..strip_config.global_end_position {
|
||||||
let mut r = 0.0;
|
let mut r = 0.0;
|
||||||
let mut g = 0.0;
|
let mut g = 0.0;
|
||||||
let mut b = 0.0;
|
let mut b = 0.0;
|
||||||
@ -88,24 +80,15 @@ impl Screenshot {
|
|||||||
}
|
}
|
||||||
return Ok(colors);
|
return Ok(colors);
|
||||||
}
|
}
|
||||||
None => Ok(vec![]),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn set_bitmap(&mut self, bitmap: Vec<u8>) {
|
|
||||||
let mut self_bitmap = self.bitmap.lock().await;
|
|
||||||
*self_bitmap = Some(bitmap);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn to_webp_base64(&self) -> String {
|
pub async fn to_webp_base64(&self) -> String {
|
||||||
let bitmap = self.bitmap.lock().await;
|
let bitmap = &self.bitmap;
|
||||||
match bitmap.to_owned() {
|
let mut bitflipped =
|
||||||
Some(bitmap) => {
|
Vec::with_capacity(self.config.display_width * self.config.display_height * 3);
|
||||||
let mut bitflipped = Vec::with_capacity(self.width * self.height * 3);
|
let stride = bitmap.len() / self.config.display_height;
|
||||||
let stride = bitmap.len() / self.height;
|
|
||||||
|
|
||||||
for y in 0..self.height {
|
for y in 0..self.config.display_height {
|
||||||
for x in 0..self.width {
|
for x in 0..self.config.display_width {
|
||||||
let i = stride * y + 4 * x;
|
let i = stride * y + 4 * x;
|
||||||
bitflipped.extend_from_slice(&[bitmap[i + 2], bitmap[i + 1], bitmap[i]]);
|
bitflipped.extend_from_slice(&[bitmap[i + 2], bitmap[i + 1], bitmap[i]]);
|
||||||
}
|
}
|
||||||
@ -113,15 +96,12 @@ impl Screenshot {
|
|||||||
|
|
||||||
let webp_memory = webp::Encoder::from_rgb(
|
let webp_memory = webp::Encoder::from_rgb(
|
||||||
bitflipped.as_slice(),
|
bitflipped.as_slice(),
|
||||||
self.width as u32,
|
self.config.display_width as u32,
|
||||||
self.height as u32,
|
self.config.display_height as u32,
|
||||||
)
|
)
|
||||||
.encode(100.0);
|
.encode(100.0);
|
||||||
return base64::encode(&*webp_memory);
|
return base64::encode(&*webp_memory);
|
||||||
}
|
}
|
||||||
None => "".to_owned(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
enum XPosition {
|
enum XPosition {
|
||||||
|
Loading…
Reference in New Issue
Block a user