2022-02-01 11:48:42 +00:00
|
|
|
#![windows_subsystem = "windows"]
|
|
|
|
|
2022-05-07 12:16:30 +00:00
|
|
|
use neutauri_data as data;
|
2022-02-05 10:05:04 +00:00
|
|
|
use std::path::PathBuf;
|
2022-01-26 17:02:17 +00:00
|
|
|
use wry::{
|
|
|
|
application::{
|
2022-01-30 05:04:26 +00:00
|
|
|
dpi::{PhysicalSize, Size},
|
2022-11-28 11:47:41 +00:00
|
|
|
event::{Event, WindowEvent},
|
2022-01-30 05:04:26 +00:00
|
|
|
event_loop::{ControlFlow, EventLoop},
|
2022-01-29 14:06:03 +00:00
|
|
|
window::{Fullscreen, Icon, Window, WindowBuilder},
|
2022-01-26 17:02:17 +00:00
|
|
|
},
|
2022-04-30 15:05:43 +00:00
|
|
|
webview::{WebContext, WebViewBuilder},
|
2022-01-26 17:02:17 +00:00
|
|
|
};
|
|
|
|
|
2022-11-28 11:47:41 +00:00
|
|
|
const PROTOCOL_PREFIX: &str = "neu://localhost";
|
2022-01-29 14:06:03 +00:00
|
|
|
const PROTOCOL: &str = "neu";
|
2022-01-26 17:02:17 +00:00
|
|
|
|
2022-11-28 11:47:41 +00:00
|
|
|
fn custom_protocol_uri<T: Into<String>>(path: T) -> String {
|
|
|
|
PROTOCOL_PREFIX.to_owned() + &path.into()
|
2022-01-26 17:02:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
fn main() -> wry::Result<()> {
|
2022-01-29 14:06:03 +00:00
|
|
|
let res = match data::load(std::env::current_exe()?.as_path()) {
|
|
|
|
Ok(data) => data,
|
2022-02-03 17:08:15 +00:00
|
|
|
Err(_) => data::load("data.neu")?,
|
2022-01-29 14:06:03 +00:00
|
|
|
};
|
2022-01-26 17:02:17 +00:00
|
|
|
let event_loop = EventLoop::new();
|
2022-01-29 14:06:03 +00:00
|
|
|
|
|
|
|
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,
|
|
|
|
};
|
2022-05-01 11:59:22 +00:00
|
|
|
let window_builder = match res.window_attr.icon {
|
2022-01-29 14:06:03 +00:00
|
|
|
Some(ref icon) => window_builder.with_window_icon(Some(Icon::from_rgba(
|
|
|
|
icon.rgba.clone(),
|
|
|
|
icon.width,
|
|
|
|
icon.height,
|
|
|
|
)?)),
|
|
|
|
None => window_builder,
|
|
|
|
};
|
2022-07-01 17:20:37 +00:00
|
|
|
let monitor_size = event_loop
|
|
|
|
.primary_monitor()
|
|
|
|
.unwrap_or_else(|| {
|
|
|
|
event_loop
|
|
|
|
.available_monitors()
|
|
|
|
.next()
|
|
|
|
.expect("no monitor found")
|
|
|
|
})
|
|
|
|
.size();
|
2022-01-29 14:06:03 +00:00
|
|
|
let window_builder = match res.window_attr.inner_size {
|
2022-01-30 05:04:26 +00:00
|
|
|
Some(size) => window_builder.with_inner_size(get_size(size, monitor_size)),
|
2022-01-29 14:06:03 +00:00
|
|
|
None => window_builder,
|
|
|
|
};
|
|
|
|
let window_builder = match res.window_attr.max_inner_size {
|
2022-01-30 05:04:26 +00:00
|
|
|
Some(size) => window_builder.with_max_inner_size(get_size(size, monitor_size)),
|
2022-01-29 14:06:03 +00:00
|
|
|
None => window_builder,
|
|
|
|
};
|
|
|
|
let window_builder = match res.window_attr.min_inner_size {
|
2022-01-30 05:04:26 +00:00
|
|
|
Some(size) => window_builder.with_min_inner_size(get_size(size, monitor_size)),
|
2022-01-29 14:06:03 +00:00
|
|
|
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) => {
|
2022-01-30 05:04:26 +00:00
|
|
|
if url.starts_with('/') {
|
2022-11-28 11:47:41 +00:00
|
|
|
webview_builder.with_url(&custom_protocol_uri(&url))?
|
2022-01-29 14:06:03 +00:00
|
|
|
} else {
|
|
|
|
webview_builder.with_url(&url)?
|
|
|
|
}
|
|
|
|
}
|
2022-11-28 11:47:41 +00:00
|
|
|
None => webview_builder.with_url(&custom_protocol_uri("/index.html"))?,
|
2022-01-29 14:06:03 +00:00
|
|
|
};
|
|
|
|
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(
|
2022-04-30 15:05:43 +00:00
|
|
|
r#"window.addEventListener('load', function(event) { window.ipc.postMessage('show_window'); });"#,
|
2022-01-29 14:06:03 +00:00
|
|
|
),
|
|
|
|
};
|
2022-02-03 16:03:50 +00:00
|
|
|
let path = std::env::current_exe()?;
|
2022-02-03 17:08:15 +00:00
|
|
|
let path = path.file_stem().unwrap_or_else(|| "neutauri_app".as_ref());
|
2022-02-05 10:05:04 +00:00
|
|
|
let mut web_context = if cfg!(target_os = "windows") {
|
|
|
|
let config_path = match std::env::var("APPDATA") {
|
|
|
|
Ok(dir) => PathBuf::from(dir),
|
|
|
|
Err(_) => PathBuf::from("."),
|
|
|
|
}
|
|
|
|
.join(path);
|
|
|
|
WebContext::new(Some(config_path))
|
|
|
|
} else if cfg!(target_os = "linux") {
|
|
|
|
let config_path = match std::env::var("XDG_CONFIG_DIR") {
|
|
|
|
Ok(dir) => PathBuf::from(dir),
|
|
|
|
Err(_) => match std::env::var("HOME") {
|
|
|
|
Ok(dir) => PathBuf::from(dir).join(".config"),
|
|
|
|
Err(_) => PathBuf::from("."),
|
|
|
|
},
|
|
|
|
}
|
|
|
|
.join(path);
|
2022-02-03 16:03:50 +00:00
|
|
|
WebContext::new(Some(config_path))
|
2022-02-05 10:05:04 +00:00
|
|
|
} else if cfg!(target_os = "macos") {
|
|
|
|
let config_path = match std::env::var("HOME") {
|
|
|
|
Ok(dir) => PathBuf::from(dir).join("Library/Application Support/"),
|
|
|
|
Err(_) => PathBuf::from("."),
|
|
|
|
}
|
|
|
|
.join(path);
|
2022-02-03 16:03:50 +00:00
|
|
|
WebContext::new(Some(config_path))
|
|
|
|
} else {
|
|
|
|
WebContext::new(None)
|
|
|
|
};
|
|
|
|
let webview = webview_builder
|
2022-07-30 15:15:33 +00:00
|
|
|
.with_clipboard(true)
|
2022-01-29 14:06:03 +00:00
|
|
|
.with_visible(res.window_attr.visible)
|
|
|
|
.with_transparent(res.window_attr.transparent)
|
2022-02-03 16:03:50 +00:00
|
|
|
.with_web_context(&mut web_context)
|
2022-07-01 17:20:37 +00:00
|
|
|
.with_initialization_script(
|
|
|
|
r#"window.oncontextmenu = (event) => { event.preventDefault(); }"#,
|
|
|
|
)
|
2022-01-26 17:02:17 +00:00
|
|
|
.with_custom_protocol(PROTOCOL.to_string(), move |request| {
|
2022-11-28 11:47:41 +00:00
|
|
|
let path = request.uri().path();
|
2022-01-29 14:06:03 +00:00
|
|
|
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));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
2022-11-28 11:47:41 +00:00
|
|
|
wry::http::Response::builder()
|
|
|
|
.header("Content-Type", file.mimetype())
|
2022-01-26 17:02:17 +00:00
|
|
|
.body(file.decompressed_data()?)
|
2022-11-28 11:47:41 +00:00
|
|
|
.map_err(|e| e.into())
|
2022-01-26 17:02:17 +00:00
|
|
|
})
|
2022-04-30 15:05:43 +00:00
|
|
|
.with_ipc_handler(|window: &Window, req: String| {
|
|
|
|
match req.as_str() {
|
2022-01-29 14:06:03 +00:00
|
|
|
"show_window" => window.set_visible(true),
|
|
|
|
"ping" => println!("recived a ping"),
|
|
|
|
_ => (),
|
|
|
|
};
|
|
|
|
})
|
2022-04-30 15:05:43 +00:00
|
|
|
.with_devtools(false)
|
2022-01-26 17:02:17 +00:00
|
|
|
.build()?;
|
|
|
|
|
|
|
|
event_loop.run(move |event, _, control_flow| {
|
|
|
|
*control_flow = ControlFlow::Wait;
|
|
|
|
|
|
|
|
match event {
|
|
|
|
Event::WindowEvent {
|
|
|
|
event: WindowEvent::CloseRequested,
|
|
|
|
..
|
|
|
|
} => *control_flow = ControlFlow::Exit,
|
2022-11-28 11:47:41 +00:00
|
|
|
Event::GlobalShortcutEvent(id) => webview
|
|
|
|
.evaluate_script(&format!("GlobalShortcutEvent({:})", id.0))
|
|
|
|
.unwrap_or_default(),
|
2022-07-01 17:20:37 +00:00
|
|
|
_ => (),
|
2022-01-26 17:02:17 +00:00
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
2022-01-30 05:04:26 +00:00
|
|
|
|
|
|
|
fn get_size(size: data::WindowSize, monitor_size: PhysicalSize<u32>) -> Size {
|
|
|
|
let (width, height) = match size {
|
|
|
|
data::WindowSize::Large => (
|
|
|
|
monitor_size.width as f64 * 0.7,
|
|
|
|
monitor_size.height as f64 * 0.7,
|
|
|
|
),
|
|
|
|
data::WindowSize::Medium => (
|
|
|
|
monitor_size.width as f64 * 0.6,
|
|
|
|
monitor_size.height as f64 * 0.6,
|
|
|
|
),
|
|
|
|
data::WindowSize::Small => (
|
|
|
|
monitor_size.width as f64 * 0.5,
|
|
|
|
monitor_size.height as f64 * 0.5,
|
|
|
|
),
|
|
|
|
data::WindowSize::Fixed { width, height } => (width, height),
|
|
|
|
data::WindowSize::Scale { factor } => (
|
|
|
|
monitor_size.width as f64 * factor,
|
|
|
|
monitor_size.height as f64 * factor,
|
|
|
|
),
|
|
|
|
};
|
|
|
|
Size::Physical(PhysicalSize::new(width as u32, height as u32))
|
|
|
|
}
|