mirror of
https://github.com/nimbold/Firelink.git
synced 2026-09-01 21:48:00 +00:00
fix(startup): defer Windows window activation (#37)
- Keep the Windows WebView host hidden and unfocused during native construction.\n- Queue deep-link, torrent, single-instance, and extension restore requests until Tauri is ready.\n- Reveal and focus the main window through one startup-safe path.\n\nFixes #37.
This commit is contained in:
@@ -308,18 +308,16 @@ async fn download_handler(
|
||||
None => return Err(StatusCode::BAD_REQUEST),
|
||||
};
|
||||
|
||||
if let Some(window) = state.app_handle.get_webview_window("main") {
|
||||
let is_visible = window.is_visible().unwrap_or(true);
|
||||
if !is_visible {
|
||||
let _ = window.show();
|
||||
let _ = window.set_focus();
|
||||
// Sleep briefly to let the webview wake up from macOS App Nap
|
||||
// otherwise the IPC event emitted immediately after is dropped.
|
||||
tokio::time::sleep(std::time::Duration::from_millis(300)).await;
|
||||
} else {
|
||||
let _ = window.show();
|
||||
let _ = window.set_focus();
|
||||
}
|
||||
let is_hidden = state
|
||||
.app_handle
|
||||
.get_webview_window("main")
|
||||
.and_then(|window| window.is_visible().ok())
|
||||
.is_some_and(|is_visible| !is_visible);
|
||||
crate::restore_main_window(&state.app_handle);
|
||||
if is_hidden {
|
||||
// Sleep briefly to let the webview wake up from macOS App Nap
|
||||
// otherwise the IPC event emitted immediately after is dropped.
|
||||
tokio::time::sleep(std::time::Duration::from_millis(300)).await;
|
||||
}
|
||||
|
||||
if !wait_for_frontend(&state.frontend_ready).await {
|
||||
|
||||
+39
-17
@@ -3659,6 +3659,7 @@ pub struct AppState {
|
||||
#[derive(Default)]
|
||||
struct MainWindowRestoreState {
|
||||
requested: AtomicBool,
|
||||
startup_complete: AtomicBool,
|
||||
}
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
@@ -3954,7 +3955,26 @@ where
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn restore_main_window(app_handle: &tauri::AppHandle) {
|
||||
pub(crate) fn restore_main_window(app_handle: &tauri::AppHandle) {
|
||||
let startup_complete = app_handle
|
||||
.try_state::<MainWindowRestoreState>()
|
||||
.is_none_or(|state| state.startup_complete.load(Ordering::Acquire));
|
||||
if !startup_complete {
|
||||
if let Some(state) = app_handle.try_state::<MainWindowRestoreState>() {
|
||||
// The first window can be requested by a single-instance callback
|
||||
// or an opened .torrent path while the native host is still being
|
||||
// constructed. Do not touch the HWND until RunEvent::Ready.
|
||||
state.requested.store(true, Ordering::Release);
|
||||
// Ready may have won the transition between the first load and
|
||||
// the request store. Re-check before returning so that request is
|
||||
// serviced by this call instead of being left pending forever.
|
||||
if !state.startup_complete.load(Ordering::Acquire) {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
let Some(window) = app_handle.get_webview_window("main") else {
|
||||
if let Some(state) = app_handle.try_state::<MainWindowRestoreState>() {
|
||||
state.requested.store(true, Ordering::Release);
|
||||
@@ -3967,6 +3987,12 @@ fn restore_main_window(app_handle: &tauri::AppHandle) {
|
||||
let _ = window.set_focus();
|
||||
}
|
||||
|
||||
fn mark_main_window_startup_complete(app_handle: &tauri::AppHandle) {
|
||||
if let Some(state) = app_handle.try_state::<MainWindowRestoreState>() {
|
||||
state.startup_complete.store(true, Ordering::Release);
|
||||
}
|
||||
}
|
||||
|
||||
fn restore_pending_main_window(app_handle: &tauri::AppHandle) {
|
||||
if app_handle
|
||||
.try_state::<MainWindowRestoreState>()
|
||||
@@ -18514,15 +18540,11 @@ pub fn run() {
|
||||
.prevent_overflow();
|
||||
#[cfg(target_os = "windows")]
|
||||
{
|
||||
// WebView2 can return E_INVALIDARG from MoveFocus while the
|
||||
// newly-created host window is not focusable yet. Wry
|
||||
// propagates that error from WebviewWindowBuilder::build,
|
||||
// which destroys the native window and makes a release build
|
||||
// look like it started headlessly before exiting. Leave the
|
||||
// window unfocused until Windows has completed native
|
||||
// activation; a synchronous post-build focus request can
|
||||
// re-enter the WebView2 focus callback on affected systems.
|
||||
main_window_builder = main_window_builder.focused(false);
|
||||
// Wry installs its parent WM_SETFOCUS handler while WebView2
|
||||
// is still being initialized. Keep the host unfocused and
|
||||
// hidden until RunEvent::Ready so Windows cannot re-enter that
|
||||
// handler during native construction.
|
||||
main_window_builder = main_window_builder.visible(false).focused(false);
|
||||
}
|
||||
main_window_builder
|
||||
.build()
|
||||
@@ -18535,13 +18557,6 @@ pub fn run() {
|
||||
collect_opened_torrent_paths(std::env::args_os().skip(1)),
|
||||
);
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
if let Some(window) = app.get_webview_window("main") {
|
||||
window
|
||||
.set_decorations(false)
|
||||
.map_err(|error| format!("failed to disable Windows native frame: {error}"))?;
|
||||
}
|
||||
|
||||
let deep_link_app = app.handle().clone();
|
||||
#[cfg(target_os = "linux")]
|
||||
if let Err(error) = app.deep_link().register_all() {
|
||||
@@ -19866,6 +19881,13 @@ pub fn run() {
|
||||
.build(tauri::generate_context!())
|
||||
.expect("error while building tauri application")
|
||||
.run(|app_handle, event| match event {
|
||||
tauri::RunEvent::Ready => {
|
||||
mark_main_window_startup_complete(app_handle);
|
||||
if let Some(state) = app_handle.try_state::<MainWindowRestoreState>() {
|
||||
state.requested.swap(false, Ordering::AcqRel);
|
||||
}
|
||||
restore_main_window(app_handle);
|
||||
}
|
||||
#[cfg(target_os = "macos")]
|
||||
tauri::RunEvent::Opened { urls } => {
|
||||
let paths = collect_opened_torrent_paths(
|
||||
|
||||
Reference in New Issue
Block a user