Files
dioxus/packages/desktop/src/launch.rs
T
Jonathan Kelley 0982c1ff8b feat: copy frameworks to Frameworks folder, detect linkers, custom target dirs (#4174)
* feat: copy frameworks to Frameworks folder

* add dylib linking to patching

* remove logging

* rename __aslr_offset

* fix android/linux

* support linker flavor

* support custom target dirs

* custom cargo target dir support

* whoops ios

* dlsym doesn't exist on windows

* look for the right main symbol per platform

* distinction between mains

* export main on windows

* enbable rlib caching

* add dx hash

* adjust linking warnings

* update tui with hotreload state

* implement symbol flags

* add flags

* only run ranlib on darwin

* enable lld

* paths

* add harness test for tls

* wip: revert launch mobile

* wip

* fix fullstack mobile

* fix a few bugs
- long paths?
- reverting back to original

* pass-thru fuse-ld args

* fix nan in render, fix empty archives

* consider existing dep files if archive exists

* fmt

* drop msrv
2025-05-24 22:41:30 +01:00

379 lines
16 KiB
Rust

// use crate::Config;
// use crate::{
// app::App,
// ipc::{IpcMethod, UserWindowEvent},
// };
// use dioxus_core::*;
// use dioxus_document::eval;
// use std::any::Any;
// use tao::event::{Event, StartCause, WindowEvent};
// /// Launches the WebView and runs the event loop
// pub fn launch(root: fn() -> Element) {
// launch_cfg(root, vec![], vec![]);
// }
// /// Launches the WebView and runs the event loop, with configuration and root props.
// pub fn launch_cfg(
// root: fn() -> Element,
// contexts: Vec<Box<dyn Fn() -> Box<dyn Any> + Send + Sync>>,
// platform_config: Vec<Box<dyn Any>>,
// ) {
// let mut virtual_dom = VirtualDom::new(root);
// for context in contexts {
// virtual_dom.insert_any_root_context(context());
// }
// let platform_config = *platform_config
// .into_iter()
// .find_map(|cfg| cfg.downcast::<Config>().ok())
// .unwrap_or_default();
// launch_virtual_dom(virtual_dom, platform_config)
// }
// /// Launches the WebView and runs the event loop, with configuration and root props.
// pub fn launch_virtual_dom(virtual_dom: VirtualDom, desktop_config: Config) -> ! {
// #[cfg(feature = "tokio_runtime")]
// {
// tokio::runtime::Builder::new_multi_thread()
// .enable_all()
// .build()
// .unwrap()
// .block_on(tokio::task::unconstrained(async move {
// launch_virtual_dom_blocking(virtual_dom, desktop_config)
// }));
// unreachable!("The desktop launch function will never exit")
// }
// #[cfg(not(feature = "tokio_runtime"))]
// {
// launch_virtual_dom_blocking(virtual_dom, desktop_config);
// }
// }
// /// Launch the WebView and run the event loop, with configuration and root props.
// ///
// /// This will block the main thread, and *must* be spawned on the main thread. This function does not assume any runtime
// /// and is equivalent to calling launch_with_props with the tokio feature disabled.
// pub fn launch_virtual_dom_blocking(virtual_dom: VirtualDom, mut desktop_config: Config) -> ! {
// let mut custom_event_handler = desktop_config.custom_event_handler.take();
// let (event_loop, mut app) = App::new(desktop_config, virtual_dom);
// event_loop.run(move |window_event, event_loop, control_flow| {
// // Set the control flow and check if any events need to be handled in the app itself
// app.tick(&window_event);
// if let Some(ref mut f) = custom_event_handler {
// f(&window_event, event_loop)
// }
// match window_event {
// Event::NewEvents(StartCause::Init) => app.handle_start_cause_init(),
// Event::LoopDestroyed => app.handle_loop_destroyed(),
// Event::WindowEvent {
// event, window_id, ..
// } => match event {
// WindowEvent::CloseRequested => app.handle_close_requested(window_id),
// WindowEvent::Destroyed { .. } => app.window_destroyed(window_id),
// WindowEvent::Resized(new_size) => app.resize_window(window_id, new_size),
// _ => {}
// },
// Event::UserEvent(event) => match event {
// UserWindowEvent::Poll(id) => app.poll_vdom(id),
// UserWindowEvent::NewWindow => app.handle_new_window(),
// UserWindowEvent::CloseWindow(id) => app.handle_close_msg(id),
// UserWindowEvent::Shutdown => app.control_flow = tao::event_loop::ControlFlow::Exit,
// #[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))]
// UserWindowEvent::GlobalHotKeyEvent(evnt) => app.handle_global_hotkey(evnt),
// #[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))]
// UserWindowEvent::MudaMenuEvent(evnt) => app.handle_menu_event(evnt),
// #[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))]
// UserWindowEvent::TrayMenuEvent(evnt) => app.handle_tray_menu_event(evnt),
// #[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))]
// UserWindowEvent::TrayIconEvent(evnt) => app.handle_tray_icon_event(evnt),
// #[cfg(all(feature = "devtools", debug_assertions))]
// UserWindowEvent::HotReloadEvent(msg) => app.handle_hot_reload_msg(msg),
// // Windows-only drag-n-drop fix events. We need to call the interpreter drag-n-drop code.
// UserWindowEvent::WindowsDragDrop(id) => {
// if let Some(webview) = app.webviews.get(&id) {
// webview.dom.in_runtime(|| {
// ScopeId::ROOT.in_runtime(|| {
// eval("window.interpreter.handleWindowsDragDrop();");
// });
// });
// }
// }
// UserWindowEvent::WindowsDragLeave(id) => {
// if let Some(webview) = app.webviews.get(&id) {
// webview.dom.in_runtime(|| {
// ScopeId::ROOT.in_runtime(|| {
// eval("window.interpreter.handleWindowsDragLeave();");
// });
// });
// }
// }
// UserWindowEvent::WindowsDragOver(id, x_pos, y_pos) => {
// if let Some(webview) = app.webviews.get(&id) {
// webview.dom.in_runtime(|| {
// ScopeId::ROOT.in_runtime(|| {
// let e = eval(
// r#"
// const xPos = await dioxus.recv();
// const yPos = await dioxus.recv();
// window.interpreter.handleWindowsDragOver(xPos, yPos)
// "#,
// );
// _ = e.send(x_pos);
// _ = e.send(y_pos);
// });
// });
// }
// }
// UserWindowEvent::Ipc { id, msg } => match msg.method() {
// IpcMethod::Initialize => app.handle_initialize_msg(id),
// IpcMethod::FileDialog => app.handle_file_dialog_msg(msg, id),
// IpcMethod::UserEvent => {}
// IpcMethod::Query => app.handle_query_msg(msg, id),
// IpcMethod::BrowserOpen => app.handle_browser_open(msg),
// IpcMethod::Other(_) => {}
// },
// },
// _ => {}
// }
// *control_flow = app.control_flow;
// })
// }
// /// Expose the `Java_dev_dioxus_main_WryActivity_create` function to the JNI layer.
// /// We hardcode these to have a single trampoline for host Java code to call into.
// ///
// /// This saves us from having to plumb the top-level package name all the way down into
// /// this file. This is better for modularity (ie just call dioxus' main to run the app) as
// /// well as cache thrashing since this crate doesn't rely on external env vars.
// ///
// /// The CLI is expecting to find `dev.dioxus.main` in the final library. If you find a need to
// /// change this, you'll need to change the CLI as well.
// #[cfg(target_os = "android")]
// #[no_mangle]
// #[inline(never)]
// pub extern "C" fn start_app() {
// wry::android_binding!(dev_dioxus, main, wry);
// tao::android_binding!(
// dev_dioxus,
// main,
// WryActivity,
// wry::android_setup,
// dioxus_main_root_fn,
// tao
// );
// }
// #[cfg(target_os = "android")]
// #[doc(hidden)]
// pub fn dioxus_main_root_fn() {
// // we're going to find the `main` symbol using dlsym directly and call it
// unsafe {
// let mut main_fn_ptr = libc::dlsym(libc::RTLD_DEFAULT, b"main\0".as_ptr() as _);
// if main_fn_ptr.is_null() {
// main_fn_ptr = libc::dlsym(libc::RTLD_DEFAULT, b"_main\0".as_ptr() as _);
// }
// if main_fn_ptr.is_null() {
// panic!("Failed to find main symbol");
// }
// // Set the env vars that rust code might expect, passed off to us by the android app
// // Doing this before main emulates the behavior of a regular executable
// if cfg!(target_os = "android") && cfg!(debug_assertions) {
// load_env_file_from_session_cache();
// }
// let main_fn: extern "C" fn() = std::mem::transmute(main_fn_ptr);
// main_fn();
// };
// }
// /// Load the env file from the session cache if we're in debug mode and on android
// ///
// /// This is a slightly hacky way of being able to use std::env::var code in android apps without
// /// going through their custom java-based system.
// #[cfg(target_os = "android")]
// fn load_env_file_from_session_cache() {
// let env_file = dioxus_cli_config::android_session_cache_dir().join(".env");
// if let Some(env_file) = std::fs::read_to_string(&env_file).ok() {
// for line in env_file.lines() {
// if let Some((key, value)) = line.trim().split_once('=') {
// std::env::set_var(key, value);
// }
// }
// }
// }
use crate::Config;
use crate::{
app::App,
ipc::{IpcMethod, UserWindowEvent},
};
use dioxus_core::*;
use dioxus_document::eval;
use std::any::Any;
use tao::event::{Event, StartCause, WindowEvent};
/// Launch the WebView and run the event loop, with configuration and root props.
///
/// This will block the main thread, and *must* be spawned on the main thread. This function does not assume any runtime
/// and is equivalent to calling launch_with_props with the tokio feature disabled.
pub fn launch_virtual_dom_blocking(virtual_dom: VirtualDom, mut desktop_config: Config) -> ! {
let mut custom_event_handler = desktop_config.custom_event_handler.take();
let (event_loop, mut app) = App::new(desktop_config, virtual_dom);
event_loop.run(move |window_event, event_loop, control_flow| {
// Set the control flow and check if any events need to be handled in the app itself
app.tick(&window_event);
if let Some(ref mut f) = custom_event_handler {
f(&window_event, event_loop)
}
match window_event {
Event::NewEvents(StartCause::Init) => app.handle_start_cause_init(),
Event::LoopDestroyed => app.handle_loop_destroyed(),
Event::WindowEvent {
event, window_id, ..
} => match event {
WindowEvent::CloseRequested => app.handle_close_requested(window_id),
WindowEvent::Destroyed { .. } => app.window_destroyed(window_id),
WindowEvent::Resized(new_size) => app.resize_window(window_id, new_size),
_ => {}
},
Event::UserEvent(event) => match event {
UserWindowEvent::Poll(id) => app.poll_vdom(id),
UserWindowEvent::NewWindow => app.handle_new_window(),
UserWindowEvent::CloseWindow(id) => app.handle_close_msg(id),
UserWindowEvent::Shutdown => app.control_flow = tao::event_loop::ControlFlow::Exit,
#[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))]
UserWindowEvent::GlobalHotKeyEvent(evnt) => app.handle_global_hotkey(evnt),
#[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))]
UserWindowEvent::MudaMenuEvent(evnt) => app.handle_menu_event(evnt),
#[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))]
UserWindowEvent::TrayMenuEvent(evnt) => app.handle_tray_menu_event(evnt),
#[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))]
UserWindowEvent::TrayIconEvent(evnt) => app.handle_tray_icon_event(evnt),
#[cfg(all(feature = "devtools", debug_assertions))]
UserWindowEvent::HotReloadEvent(msg) => app.handle_hot_reload_msg(msg),
// Windows-only drag-n-drop fix events. We need to call the interpreter drag-n-drop code.
UserWindowEvent::WindowsDragDrop(id) => {
if let Some(webview) = app.webviews.get(&id) {
webview.dom.in_runtime(|| {
ScopeId::ROOT.in_runtime(|| {
eval("window.interpreter.handleWindowsDragDrop();");
});
});
}
}
UserWindowEvent::WindowsDragLeave(id) => {
if let Some(webview) = app.webviews.get(&id) {
webview.dom.in_runtime(|| {
ScopeId::ROOT.in_runtime(|| {
eval("window.interpreter.handleWindowsDragLeave();");
});
});
}
}
UserWindowEvent::WindowsDragOver(id, x_pos, y_pos) => {
if let Some(webview) = app.webviews.get(&id) {
webview.dom.in_runtime(|| {
ScopeId::ROOT.in_runtime(|| {
let e = eval(
r#"
const xPos = await dioxus.recv();
const yPos = await dioxus.recv();
window.interpreter.handleWindowsDragOver(xPos, yPos)
"#,
);
_ = e.send(x_pos);
_ = e.send(y_pos);
});
});
}
}
UserWindowEvent::Ipc { id, msg } => match msg.method() {
IpcMethod::Initialize => app.handle_initialize_msg(id),
IpcMethod::FileDialog => app.handle_file_dialog_msg(msg, id),
IpcMethod::UserEvent => {}
IpcMethod::Query => app.handle_query_msg(msg, id),
IpcMethod::BrowserOpen => app.handle_browser_open(msg),
IpcMethod::Other(_) => {}
},
},
_ => {}
}
*control_flow = app.control_flow;
})
}
/// Launches the WebView and runs the event loop, with configuration and root props.
pub fn launch_virtual_dom(virtual_dom: VirtualDom, desktop_config: Config) -> ! {
#[cfg(feature = "tokio_runtime")]
{
tokio::runtime::Builder::new_multi_thread()
.enable_all()
.build()
.unwrap()
.block_on(tokio::task::unconstrained(async move {
launch_virtual_dom_blocking(virtual_dom, desktop_config)
}));
unreachable!("The desktop launch function will never exit")
}
#[cfg(not(feature = "tokio_runtime"))]
{
launch_virtual_dom_blocking(virtual_dom, desktop_config);
}
}
/// Launches the WebView and runs the event loop, with configuration and root props.
pub fn launch(
root: fn() -> Element,
contexts: Vec<Box<dyn Fn() -> Box<dyn Any> + Send + Sync>>,
platform_config: Vec<Box<dyn Any>>,
) -> ! {
let mut virtual_dom = VirtualDom::new(root);
for context in contexts {
virtual_dom.insert_any_root_context(context());
}
let platform_config = *platform_config
.into_iter()
.find_map(|cfg| cfg.downcast::<Config>().ok())
.unwrap_or_default();
launch_virtual_dom(virtual_dom, platform_config)
}