pref: 截图改用自定义协议传递。

This commit is contained in:
2023-03-20 09:27:47 +08:00
parent 5df7f54bed
commit 85ef261c51
7 changed files with 194 additions and 19 deletions

17
src-tauri/Cargo.lock generated
View File

@ -2653,12 +2653,14 @@ dependencies = [
"env_logger",
"log",
"paris",
"percent-encoding",
"png",
"serde",
"serde_json",
"tauri",
"tauri-build",
"tokio",
"url-build-parse",
"webp",
]
@ -2913,6 +2915,21 @@ dependencies = [
"serde",
]
[[package]]
name = "url-build-parse"
version = "9.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "434debbcbb26557225df912efe7949c6d1237defa81ae14d05a9cd8b7ffa2776"
dependencies = [
"url-search-params",
]
[[package]]
name = "url-search-params"
version = "9.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e84890540c25e90ae611bfbab7d8bfcd138f29af6495027478945c1427abf532"
[[package]]
name = "utf-8"
version = "0.7.6"

View File

@ -26,6 +26,8 @@ tokio = {version = "1.26.0", features = ["full"] }
paris = { version = "1.5", features = ["timestamps", "macros"] }
log = "0.4.17"
env_logger = "0.10.0"
percent-encoding = "2.2.0"
url-build-parse = "9.0.0"
[features]
# this feature is used for production builds or when `devPath` points to the filesystem

View File

@ -9,10 +9,11 @@ use core_graphics::display::{
kCGNullWindowID, kCGWindowImageDefault, kCGWindowListOptionOnScreenOnly, CGDisplay,
};
use display_info::DisplayInfo;
use paris::error;
use paris::{error, info};
use screenshot_manager::ScreenshotManager;
use serde::{Deserialize, Serialize};
use serde_json::to_string;
use tauri::{http::ResponseBuilder, regex};
#[derive(Serialize, Deserialize)]
#[serde(remote = "DisplayInfo")]
@ -165,6 +166,122 @@ async fn main() {
list_display_info,
subscribe_encoded_screenshot_updated
])
.register_uri_scheme_protocol("ambient-light", move |_app, request| {
info!("request: {:?}", request.uri());
// prepare our response
let response = ResponseBuilder::new().header("Access-Control-Allow-Origin", "*");
// get the file path
let uri = request.uri();
let uri = percent_encoding::percent_decode_str(uri)
.decode_utf8()
.unwrap()
.to_string();
let url = url_build_parse::parse_url(uri.as_str());
if let Err(err) = url {
error!("url parse error: {}", err);
return response
.status(500)
.mimetype("text/plain")
.body("Parse uri failed.".as_bytes().to_vec());
}
let url = url.unwrap();
let re = regex::Regex::new(r"^/displays/(\d+)$").unwrap();
let path = url.path;
let captures = re.captures(path.as_str());
if let None = captures {
error!("path not matched: {:?}", path);
return response
.status(404)
.mimetype("text/plain")
.body("Path Not Found.".as_bytes().to_vec());
}
let captures = captures.unwrap();
let display_id = captures[1].parse::<u32>().unwrap();
let bytes = tokio::task::block_in_place(move || {
tauri::async_runtime::block_on(async move {
let screenshot_manager = ScreenshotManager::global().await;
let channels = screenshot_manager.channels.read().await;
if let Some(rx) = channels.get(&display_id) {
let rx = rx.clone();
let screenshot = rx.borrow().clone();
let bytes = screenshot.bytes.read().await;
let (scale_factor, width, height) = if url.query.is_some()
&& url.query.as_ref().unwrap().contains_key("height")
&& url.query.as_ref().unwrap().contains_key("width")
{
let width =
url.query.as_ref().unwrap()["width"].parse::<u32>().unwrap();
let height = url.query.as_ref().unwrap()["height"]
.parse::<u32>()
.unwrap();
(screenshot.width as f32 / width as f32, width, height)
} else {
info!("scale by scale_factor");
let scale_factor = screenshot.scale_factor;
(
scale_factor,
(screenshot.width as f32 / scale_factor) as u32,
(screenshot.height as f32 / scale_factor) as u32,
)
};
info!(
"scale by query. width: {}, height: {}, scale_factor: {}, len: {}",
width,
height,
screenshot.width as f32 / width as f32,
width * height * 4,
);
let bytes_per_row = screenshot.bytes_per_row as f32;
let mut rgba_buffer = vec![0u8; (width * height * 4) as usize];
for y in 0..height {
for x in 0..width {
let offset = ((y as f32) * scale_factor) as usize * bytes_per_row as usize
+ ((x as f32) * scale_factor) as usize * 4;
let b = bytes[offset];
let g = bytes[offset + 1];
let r = bytes[offset + 2];
let a = bytes[offset + 3];
let offset_2 = (y * width + x) as usize * 4;
rgba_buffer[offset_2] = r;
rgba_buffer[offset_2 + 1] = g;
rgba_buffer[offset_2 + 2] = b;
rgba_buffer[offset_2 + 3] = a;
}
}
Ok(rgba_buffer.clone())
} else {
anyhow::bail!("Display#{}: not found", display_id);
}
})
});
if let Ok(bytes) = bytes {
return response
.mimetype("octet/stream")
.status(200)
.body(bytes.to_vec());
}
let err = bytes.unwrap_err();
error!("request screenshot bin data failed: {}", err);
return response
.mimetype("text/plain")
.status(500)
.body(err.to_string().into_bytes());
})
.run(tauri::generate_context!())
.expect("error while running tauri application");
}

View File

@ -44,7 +44,7 @@ pub fn take_screenshot(display_id: u32, scale_factor: f32) -> anyhow::Result<Scr
}
pub struct ScreenshotManager {
channels: Arc<RwLock<HashMap<u32, watch::Receiver<Screenshot>>>>,
pub channels: Arc<RwLock<HashMap<u32, watch::Receiver<Screenshot>>>>,
encode_listeners: Arc<RwLock<HashMap<u32, Vec<Window>>>>,
}