From acfb6b6c6b8a9f3deaddca0bc568ece382250ef7 Mon Sep 17 00:00:00 2001 From: Tim-Paik Date: Sat, 29 Jan 2022 22:06:03 +0800 Subject: [PATCH] update runner --- Cargo.lock | 242 +++++++++++++++++++++++++++++++++++ neutauri_runtime/Cargo.toml | 1 + neutauri_runtime/src/data.rs | 162 +++++++++++++++++++---- neutauri_runtime/src/main.rs | 131 +++++++++++++++++-- 4 files changed, 499 insertions(+), 37 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7f941ff..21e66c7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,18 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] +name = "adler32" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aae1277d39aeec15cb388266ecc24b11c80469deae6067e17a1a7aa9e5c1f234" + [[package]] name = "alloc-no-stdlib" version = "2.0.3" @@ -106,6 +118,18 @@ dependencies = [ "alloc-stdlib", ] +[[package]] +name = "bytemuck" +version = "1.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439989e6b8c38d1b6570a384ef1e49c8848128f5a97f3914baef02920842712f" + +[[package]] +name = "byteorder" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" + [[package]] name = "bytes" version = "1.1.0" @@ -224,6 +248,12 @@ dependencies = [ "objc", ] +[[package]] +name = "color_quant" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" + [[package]] name = "com" version = "0.2.0" @@ -337,6 +367,15 @@ dependencies = [ "objc", ] +[[package]] +name = "crc32fast" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2209c310e29876f7f0b2721e7e26b84aff178aa3da5d091f9bfbf47669e60e3" +dependencies = [ + "cfg-if 1.0.0", +] + [[package]] name = "crossbeam-channel" version = "0.5.2" @@ -347,6 +386,30 @@ dependencies = [ "crossbeam-utils", ] +[[package]] +name = "crossbeam-deque" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6455c0ca19f0d2fbf751b908d5c55c1f5cbc65e03c4225427254b46890bdde1e" +dependencies = [ + "cfg-if 1.0.0", + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97242a70df9b89a65d0b6df3c4bf5b9ce03c5b7309019777fbde37e7537f8762" +dependencies = [ + "cfg-if 1.0.0", + "crossbeam-utils", + "lazy_static", + "memoffset", + "scopeguard", +] + [[package]] name = "crossbeam-utils" version = "0.8.6" @@ -398,6 +461,16 @@ dependencies = [ "syn", ] +[[package]] +name = "deflate" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73770f8e1fe7d64df17ca66ad28994a0a623ea497fa69486e14984e715c5d174" +dependencies = [ + "adler32", + "byteorder", +] + [[package]] name = "dispatch" version = "0.2.0" @@ -560,6 +633,16 @@ dependencies = [ "system-deps 3.2.0", ] +[[package]] +name = "gif" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3a7187e78088aead22ceedeee99779455b23fc231fe13ec443f99bb71694e5b" +dependencies = [ + "color_quant", + "weezl", +] + [[package]] name = "gio" version = "0.14.8" @@ -793,6 +876,25 @@ dependencies = [ "unicode-normalization", ] +[[package]] +name = "image" +version = "0.23.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24ffcb7e7244a9bf19d35bf2883b9c080c4ced3c07a9895572178cdb8f13f6a1" +dependencies = [ + "bytemuck", + "byteorder", + "color_quant", + "gif", + "jpeg-decoder", + "num-iter", + "num-rational", + "num-traits", + "png", + "scoped_threadpool", + "tiff", +] + [[package]] name = "indexmap" version = "1.8.0" @@ -852,6 +954,15 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" +[[package]] +name = "jpeg-decoder" +version = "0.1.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "229d53d58899083193af11e15917b5640cd40b29ff475a1fe4ef725deb02d0f2" +dependencies = [ + "rayon", +] + [[package]] name = "lazy_static" version = "1.4.0" @@ -918,6 +1029,25 @@ version = "0.3.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" +[[package]] +name = "miniz_oxide" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "791daaae1ed6889560f8c4359194f56648355540573244a5448a83ba1ecc7435" +dependencies = [ + "adler32", +] + +[[package]] +name = "miniz_oxide" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a92518e98c078586bc6c934028adcca4c92a53d6a958196de835170a01d84e4b" +dependencies = [ + "adler", + "autocfg", +] + [[package]] name = "ndk" version = "0.4.0" @@ -977,6 +1107,7 @@ version = "0.1.0" dependencies = [ "bincode", "brotli", + "image", "new_mime_guess", "serde", "toml", @@ -993,6 +1124,57 @@ dependencies = [ "unicase", ] +[[package]] +name = "num-integer" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db" +dependencies = [ + "autocfg", + "num-traits", +] + +[[package]] +name = "num-iter" +version = "0.1.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2021c8337a54d21aca0d59a92577a029af9431cb59b909b03252b9c164fad59" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-rational" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12ac428b1cb17fce6f731001d307d351ec70a6d202fc2e60f7d4c5e42d8f4f07" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_cpus" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19e64526ebdee182341572e50e9ad03965aa510cd94427a4549448f285e957a1" +dependencies = [ + "hermit-abi", + "libc", +] + [[package]] name = "num_enum" version = "0.5.6" @@ -1130,6 +1312,18 @@ version = "0.3.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "58893f751c9b0412871a09abd62ecd2a00298c6c83befa223ef98c52aef40cbe" +[[package]] +name = "png" +version = "0.16.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c3287920cb847dee3de33d301c463fba14dda99db24214ddf93f83d3021f4c6" +dependencies = [ + "bitflags", + "crc32fast", + "deflate", + "miniz_oxide 0.3.7", +] + [[package]] name = "proc-macro-crate" version = "0.1.5" @@ -1210,6 +1404,31 @@ dependencies = [ "cty", ] +[[package]] +name = "rayon" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c06aca804d41dbc8ba42dfd964f0d01334eceb64314b9ecf7c5fad5188a06d90" +dependencies = [ + "autocfg", + "crossbeam-deque", + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d78120e2c850279833f1dd3582f730c4ab53ed95aeaaaa862a2a5c71b1656d8e" +dependencies = [ + "crossbeam-channel", + "crossbeam-deque", + "crossbeam-utils", + "lazy_static", + "num_cpus", +] + [[package]] name = "redox_syscall" version = "0.2.10" @@ -1234,6 +1453,12 @@ version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73b4b750c782965c211b42f022f59af1fbceabdd026623714f104152f1ec149f" +[[package]] +name = "scoped_threadpool" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d51f5df5af43ab3f1360b429fa5e0152ac5ce8c0bd6485cae490332e96846a8" + [[package]] name = "scopeguard" version = "1.1.0" @@ -1506,6 +1731,17 @@ dependencies = [ "syn", ] +[[package]] +name = "tiff" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a53f4706d65497df0c4349241deddf35f84cee19c87ed86ea8ca590f4464437" +dependencies = [ + "jpeg-decoder", + "miniz_oxide 0.4.4", + "weezl", +] + [[package]] name = "tinyvec" version = "1.5.1" @@ -1671,6 +1907,12 @@ dependencies = [ "winapi", ] +[[package]] +name = "weezl" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8b77fdfd5a253be4ab714e4ffa3c49caf146b4de743e97510c0656cf90f1e8e" + [[package]] name = "widestring" version = "0.5.1" diff --git a/neutauri_runtime/Cargo.toml b/neutauri_runtime/Cargo.toml index a08cd0b..fd9977b 100644 --- a/neutauri_runtime/Cargo.toml +++ b/neutauri_runtime/Cargo.toml @@ -6,6 +6,7 @@ version = "0.1.0" [dependencies] bincode = "1.3" brotli = "3.3" +image = "0.23" new_mime_guess = "4.0" serde = {version = "1.0", features = ["derive"]} toml = "0.5" diff --git a/neutauri_runtime/src/data.rs b/neutauri_runtime/src/data.rs index 52c78fb..2ddea1a 100644 --- a/neutauri_runtime/src/data.rs +++ b/neutauri_runtime/src/data.rs @@ -5,7 +5,7 @@ use std::{ io::{self, Read, Result, Seek, SeekFrom}, path::{self, Component, Path, PathBuf}, }; -use wry::application::dpi::{Position, Size}; +use wry::application::dpi::Position; const MAGIC_NUMBER_START: &[u8; 9] = b"NEUTFSv01"; const MAGIC_NUMBER_END: &[u8; 9] = b"NEUTFSEnd"; @@ -31,30 +31,55 @@ struct Dir { dirs: Vec<(String, Dir)>, } -#[derive(Serialize, Deserialize, Debug)] -struct Config { - window_attr: WindowAttr, - webview_attr: WebViewAttr, -} - #[derive(Serialize, Deserialize, Debug)] pub struct Data { - config: Config, + pub window_attr: WindowAttr, + pub webview_attr: WebViewAttr, fs: Dir, } +#[derive(Serialize, Deserialize, Clone, Debug)] +pub enum WindowSize { + Large, + Medium, + Small, + Fixed(f64, f64), +} + +#[derive(Serialize, Deserialize, Clone, Debug)] +pub struct Config { + pub source: PathBuf, + pub target: PathBuf, + pub inner_size: Option, + pub min_inner_size: Option, + pub max_inner_size: Option, + pub resizable: bool, + pub fullscreen: bool, + pub title: String, + pub maximized: bool, + pub visible: bool, + pub transparent: bool, + pub decorations: bool, + pub always_on_top: bool, + pub window_icon: Option, + pub spa: bool, + pub url: Option, + pub html: Option, + pub initialization_script: Option, +} + #[derive(Serialize, Deserialize, Clone, Debug)] pub struct Icon { - rgba: Vec, - width: u32, - height: u32, + pub rgba: Vec, + pub width: u32, + pub height: u32, } #[derive(Serialize, Deserialize, Clone, Debug)] pub struct WindowAttr { - pub inner_size: Option, - pub min_inner_size: Option, - pub max_inner_size: Option, + pub inner_size: Option, + pub min_inner_size: Option, + pub max_inner_size: Option, pub position: Option, pub resizable: bool, pub fullscreen: bool, @@ -71,15 +96,15 @@ pub struct WindowAttr { pub struct WebViewAttr { pub visible: bool, pub transparent: bool, + pub spa: bool, pub url: Option, pub html: Option, - pub initialization_scripts: Vec, + pub initialization_script: Option, } impl File { pub fn decompressed_data(&mut self) -> Result> { let mut data = Vec::with_capacity(self.data.len()); - //brotli::BrotliDecompress(&mut self.data, &mut data); let mut r = brotli::Decompressor::new(self.data.as_slice(), 4096); r.read_to_end(&mut data)?; Ok(data) @@ -164,10 +189,8 @@ impl Data { dir.fill_with(source, source, &mut length)?; Ok(Self { fs: dir, - config: Config { - window_attr, - webview_attr, - }, + window_attr, + webview_attr, }) } @@ -197,11 +220,20 @@ impl Data { Ok(target) } - pub fn pack>(source: P, target: P, config: P) -> Result<()> { - let config: Config = toml::from_str(fs::read_to_string(config)?.as_str())?; + pub fn pack>(config_path: P) -> Result<()> { + let config_path: &Path = config_path.as_ref().clone(); + let config: Config = toml::from_str(fs::read_to_string(config_path)?.as_str())?; + let source = &match config_path.parent() { + Some(path) => path.join(&config.source).canonicalize()?, + None => config.source.canonicalize()?, + }; + let target = &match config_path.parent() { + Some(path) => path.join(&config.target).canonicalize()?, + None => config.target.canonicalize()?, + }; fs::write( target, - Self::build_from_dir(source, config.window_attr, config.webview_attr)?, + Self::build_from_dir(source, config.window_attr()?, config.webview_attr()?)?, )?; Ok(()) } @@ -242,7 +274,6 @@ impl Data { // 此时指针指向 Data 前 base.take(u64::from_be_bytes(data_length_data)) .read_to_end(&mut data)?; - fs::write("test2", &data)?; let serialize_options = bincode::DefaultOptions::new() .with_fixint_encoding() .allow_trailing_bytes() @@ -287,12 +318,91 @@ impl Data { } } +impl Default for Config { + fn default() -> Self { + Self { + source: PathBuf::from("."), + target: PathBuf::from("app.neu"), + inner_size: Some(WindowSize::Medium), + min_inner_size: None, + max_inner_size: None, + resizable: true, + fullscreen: false, + title: "".to_string(), + maximized: false, + visible: true, + transparent: false, + decorations: true, + always_on_top: false, + window_icon: None, + spa: false, + url: Some("/index.html".to_string()), + html: None, + initialization_script: Some("".to_string()), + } + } +} + +impl Config { + pub fn window_attr(&self) -> Result { + Ok(WindowAttr { + inner_size: self.inner_size.clone(), + min_inner_size: self.min_inner_size.clone(), + max_inner_size: self.max_inner_size.clone(), + position: None, + resizable: self.resizable, + fullscreen: self.fullscreen, + title: self.title.clone(), + maximized: self.maximized, + visible: self.visible, + transparent: self.transparent, + decorations: self.decorations, + always_on_top: self.always_on_top, + window_icon: match &self.window_icon { + Some(path) => Some(load_icon(&path.as_path())?), + None => None, + }, + }) + } + pub fn webview_attr(&self) -> Result { + Ok(WebViewAttr { + visible: self.visible, + transparent: self.transparent, + spa: self.spa, + url: self.url.clone(), + html: match &self.html { + Some(path) => fs::read_to_string(path.as_path()).ok(), + None => None, + }, + initialization_script: self.initialization_script.clone(), + }) + } +} + pub fn load + Copy>(path: P) -> Result { Data::new(path) } -pub fn pack>(source: P, target: P, config: P) -> Result<()> { - Data::pack(source, target, config) +pub fn pack>(config: P) -> Result<()> { + Data::pack(config) +} + +fn load_icon(path: &Path) -> Result { + let (icon_rgba, icon_width, icon_height) = { + let image = match image::open(path) { + Ok(img) => img, + Err(e) => return Err(io::Error::new(io::ErrorKind::InvalidData, e)), + } + .into_rgba8(); + let (width, height) = image.dimensions(); + let rgba = image.into_raw(); + (rgba, width, height) + }; + Ok(Icon { + rgba: icon_rgba, + width: icon_width, + height: icon_height, + }) } fn normalize_path(path: &Path) -> PathBuf { diff --git a/neutauri_runtime/src/main.rs b/neutauri_runtime/src/main.rs index 14de6c3..0ef8efc 100644 --- a/neutauri_runtime/src/main.rs +++ b/neutauri_runtime/src/main.rs @@ -1,10 +1,10 @@ use wry::{ application::{ event::{Event, StartCause, WindowEvent}, - event_loop::{ControlFlow, EventLoop}, - window::WindowBuilder, + event_loop::{self, ControlFlow, EventLoop}, + window::{Fullscreen, Icon, Window, WindowBuilder}, }, - webview::WebViewBuilder, + webview::{RpcRequest, WebViewBuilder}, }; mod data; @@ -13,7 +13,7 @@ const PROTOCOL_PREFIX: &str = "https://{PROTOCOL}."; #[cfg(not(windows))] const PROTOCOL_PREFIX: &str = "{PROTOCOL}://"; -const PROTOCOL: &str = "neutauri"; +const PROTOCOL: &str = "neu"; fn custom_protocol_uri>(protocol: T, path: T) -> String { PROTOCOL_PREFIX.replacen("{PROTOCOL}", &protocol.into(), 1) + &path.into() @@ -32,26 +32,135 @@ fn custom_protocol_uri_to_path>(protocol: T, uri: T) -> wry::Res } fn main() -> wry::Result<()> { - let res = data::load(std::env::current_exe()?.as_path())?; + data::pack("config.toml")?; + let res = match data::load(std::env::current_exe()?.as_path()) { + Ok(data) => data, + Err(_) => data::load("app.neu")?, + }; let event_loop = EventLoop::new(); - let window = WindowBuilder::new().build(&event_loop)?; - let _webview = WebViewBuilder::new(window)? - .with_transparent(false) + + let window_builder = WindowBuilder::new() + .with_always_on_top(res.window_attr.always_on_top) + .with_decorations(res.window_attr.decorations) + .with_resizable(res.window_attr.resizable) + .with_title(res.window_attr.title.clone()) + .with_maximized(res.window_attr.maximized) + .with_transparent(res.window_attr.transparent) + .with_visible(res.window_attr.visible); + let window_builder = match res.window_attr.fullscreen { + true => window_builder.with_fullscreen(Some(Fullscreen::Borderless(None))), + false => window_builder, + }; + let window_builder = match res.window_attr.window_icon { + Some(ref icon) => window_builder.with_window_icon(Some(Icon::from_rgba( + icon.rgba.clone(), + icon.width, + icon.height, + )?)), + None => window_builder, + }; + let monitor = event_loop + .primary_monitor() + .unwrap_or_else(|| event_loop.available_monitors().next().unwrap()); + dbg!( + monitor.size(), + monitor.name(), + monitor.position(), + monitor.scale_factor(), + monitor.video_modes().collect::>() + ); + let window_builder = match res.window_attr.inner_size { + Some(size) => match size { + data::WindowSize::Large => todo!(), + data::WindowSize::Medium => todo!(), + data::WindowSize::Small => todo!(), + data::WindowSize::Fixed(width, height) => todo!(), + }, + None => window_builder, + }; + let window_builder = match res.window_attr.max_inner_size { + Some(size) => match size { + data::WindowSize::Large => todo!(), + data::WindowSize::Medium => todo!(), + data::WindowSize::Small => todo!(), + data::WindowSize::Fixed(width, height) => todo!(), + }, + None => window_builder, + }; + let window_builder = match res.window_attr.min_inner_size { + Some(size) => match size { + data::WindowSize::Large => todo!(), + data::WindowSize::Medium => todo!(), + data::WindowSize::Small => todo!(), + data::WindowSize::Fixed(width, height) => todo!(), + }, + None => window_builder, + }; + let window = window_builder.build(&event_loop)?; + + let webview_builder = WebViewBuilder::new(window)?; + let url = res.webview_attr.url.clone(); + let webview_builder = match url { + Some(url) => { + if url.starts_with("/") { + webview_builder.with_url(&custom_protocol_uri(PROTOCOL, &url))? + } else { + webview_builder.with_url(&url)? + } + } + None => webview_builder.with_url(&custom_protocol_uri(PROTOCOL, "/index.html"))?, + }; + let html = res.webview_attr.html.clone(); + let webview_builder = match html { + Some(html) => webview_builder.with_html(&html)?, + None => webview_builder, + }; + let initialization_script = res.webview_attr.initialization_script.clone(); + let webview_builder = match initialization_script { + Some(script) => webview_builder.with_initialization_script(&script), + None => webview_builder, + }; + let webview_builder = match res.window_attr.visible { + true => webview_builder.with_visible(true), + false => webview_builder + .with_visible(false) + .with_initialization_script( + r#"window.addEventListener('load', function(event) { rpc.call('show_window'); });"#, + ), + }; + let _webview = webview_builder + .with_visible(res.window_attr.visible) + .with_transparent(res.window_attr.transparent) .with_custom_protocol(PROTOCOL.to_string(), move |request| { let path = custom_protocol_uri_to_path(PROTOCOL, request.uri())?; - let mut file = res.open(path)?; + let mut file = match res.open(path) { + Ok(file) => file, + Err(e) => { + if e.kind() == std::io::ErrorKind::NotFound && res.webview_attr.spa { + res.open("index.html")? + } else { + return Err(wry::Error::Io(e)); + } + } + }; wry::http::ResponseBuilder::new() .mimetype(&file.mimetype()) .body(file.decompressed_data()?) }) - .with_url(&custom_protocol_uri(PROTOCOL, "/index.html"))? + .with_rpc_handler(|window: &Window, req: RpcRequest| { + match req.method.as_str() { + "show_window" => window.set_visible(true), + "ping" => println!("recived a ping"), + _ => (), + }; + None + }) .build()?; event_loop.run(move |event, _, control_flow| { *control_flow = ControlFlow::Wait; match event { - Event::NewEvents(StartCause::Init) => println!("Wry has started!"), Event::WindowEvent { event: WindowEvent::CloseRequested, ..