fix(tauri): retry main-window lookup on Windows after SW_SHOW (#3A) (#2341)

This commit is contained in:
oxoxDev
2026-05-22 15:59:05 +05:30
committed by GitHub
parent 002dc8d76d
commit b08aa3ea8d
+58 -3
View File
@@ -1169,6 +1169,59 @@ fn set_main_window_hidden(hide: bool) {
);
}
/// Look up the main `WebviewWindow`, optionally waiting briefly on Windows
/// for the Tauri runtime to re-track the window after SW_SHOW.
///
/// Why this exists (OPENHUMAN-TAURI-3A): on Windows the close button routes
/// through [`set_main_window_hidden`] which uses raw-HWND `SW_HIDE`. CEF
/// treats the hidden host as gone and the Tauri runtime drops its
/// `WebviewWindow` record for `"main"` until the next event-loop tick after
/// SW_SHOW restores visibility. A tray "Show window" callback that runs
/// `set_main_window_hidden(false)` and then immediately calls
/// `app.get_webview_window("main")` can race the re-track step and observe
/// `None` even though the OS window is visible — Sentry sees a
/// `[tray] failed to show main window from menu: main window not found`
/// warn even though, from the user's perspective, the window came back.
///
/// Bounded retry budget: up to 5 lookups with 10 ms between attempts (≤ 50 ms
/// worst case). The tray menu is closed during this window, so the small
/// blocking delay is invisible. After the budget expires the original
/// error path still triggers, preserving the signal if the runtime never
/// re-tracks (which would indicate a real lifecycle bug, not a race).
///
/// Non-Windows platforms use a single lookup — the close-to-tray flow that
/// produces the race is Windows-specific (the macOS close button routes
/// through `app.hide()` per PR #2049, and Linux/X11 keeps the
/// `WebviewWindow` record across `WM_DELETE_WINDOW` handling).
fn get_main_webview_window_with_retry(
app: &AppHandle<AppRuntime>,
) -> Option<tauri::WebviewWindow<AppRuntime>> {
#[cfg(target_os = "windows")]
{
const ATTEMPTS: usize = 5;
const BACKOFF: std::time::Duration = std::time::Duration::from_millis(10);
for attempt in 0..ATTEMPTS {
if let Some(window) = app.get_webview_window("main") {
if attempt > 0 {
log::debug!(
"[show_main_window] runtime re-tracked main window after {} retries",
attempt
);
}
return Some(window);
}
if attempt + 1 < ATTEMPTS {
std::thread::sleep(BACKOFF);
}
}
None
}
#[cfg(not(target_os = "windows"))]
{
app.get_webview_window("main")
}
}
fn show_main_window(app: &AppHandle<AppRuntime>) -> Result<(), String> {
// On Windows: surface the OS top-level Chrome_WidgetWin_1 frame BEFORE
// any Tauri lookups. After our close handler's SW_HIDE the runtime
@@ -1177,7 +1230,10 @@ fn show_main_window(app: &AppHandle<AppRuntime>) -> Result<(), String> {
// and the early `?` below would abort before SW_SHOW fires (#1607).
// EnumWindows + SW_SHOW operates directly on the OS HWND that
// survived independently, and the runtime re-tracks the window once
// it's visible again.
// it's visible again — but re-tracking lands on the next event-loop
// tick, not synchronously with SW_SHOW. `get_main_webview_window_with_retry`
// bounds the wait to ~50 ms total so the tray callback can pick up the
// re-tracked window without re-emitting OPENHUMAN-TAURI-3A.
#[cfg(target_os = "windows")]
{
set_main_window_hidden(false);
@@ -1186,8 +1242,7 @@ fn show_main_window(app: &AppHandle<AppRuntime>) -> Result<(), String> {
let _ = webview.set_focus();
}
}
let window = app
.get_webview_window("main")
let window = get_main_webview_window_with_retry(app)
.ok_or_else(|| "main window not found".to_string())?;
window
.show()