diff --git a/apps/desktop/src-tauri/src/desktop.rs b/apps/desktop/src-tauri/src/desktop.rs index c700bea..eb05110 100644 --- a/apps/desktop/src-tauri/src/desktop.rs +++ b/apps/desktop/src-tauri/src/desktop.rs @@ -36,6 +36,7 @@ struct DesktopRuntime { shutdown: CancellationToken, server: Mutex>>>, exiting: AtomicBool, + server_addr: std::net::SocketAddr, } #[tauri::command] @@ -141,6 +142,21 @@ fn create_main_window( builder.build() } +/// 按需打开主窗口:webview 仅在需要界面时创建,关闭窗口即销毁释放内存。 +pub(crate) fn open_main_window(app: &AppHandle) -> tauri::Result<()> { + if let Some(window) = app.get_webview_window(MAIN_WINDOW_LABEL) { + let _ = window.unminimize(); + let _ = window.show(); + let _ = window.set_focus(); + return Ok(()); + } + let address = app.state::().server_addr; + let window = create_main_window(app, address)?; + window.show()?; + window.set_focus()?; + Ok(()) +} + pub fn run() -> ExitCode { let diagnostics = match StartupDiagnostics::initialize() { Ok(diagnostics) => diagnostics, @@ -184,7 +200,7 @@ pub fn run() -> ExitCode { ]) .plugin(tauri_plugin_single_instance::init(|app, args, _| { if !args.iter().any(|arg| arg == AUTOSTART_ARG) { - tray::show_main_window(app); + let _ = open_main_window(app); } })) .plugin(tauri_plugin_clipboard_manager::init()) @@ -240,13 +256,12 @@ pub fn run() -> ExitCode { shutdown, server: Mutex::new(Some(task)), exiting: AtomicBool::new(false), + server_addr: address, }); - let window = create_main_window(app.handle(), address)?; if desktop_settings.silent_start && started_by_autostart { - tracing::info!("silent autostart enabled; keeping the main window hidden"); + tracing::info!("silent autostart enabled; starting without the main window"); } else { - window.show()?; - window.set_focus()?; + open_main_window(app.handle())?; } tray::create(app)?; crate::update::signal_ready_if_requested()?; @@ -262,46 +277,42 @@ pub fn run() -> ExitCode { }; app.run(|app, event| match event { - RunEvent::WindowEvent { - label, - event: tauri::WindowEvent::CloseRequested { api, .. }, - .. - } if label == MAIN_WINDOW_LABEL => { - let runtime = app.state::(); - if !runtime.exiting.load(Ordering::Acquire) { - api.prevent_close(); - if let Some(window) = app.get_webview_window(MAIN_WINDOW_LABEL) { - let _ = window.hide(); + // code 为 None 表示所有窗口已被关闭(轻量模式),阻止退出, + // 转发服务继续在托盘后台运行;code 为 Some 时是显式退出请求。 + RunEvent::ExitRequested { code, api, .. } => match code { + None => api.prevent_exit(), + Some(_) => { + let runtime = app.state::(); + if !runtime.exiting.swap(true, Ordering::AcqRel) { + api.prevent_exit(); + runtime.shutdown.cancel(); + let server = runtime.server.lock().expect("server lock poisoned").take(); + let app = app.clone(); + tauri::async_runtime::spawn(async move { + if let Some(server) = server { + match tokio::time::timeout(Duration::from_secs(11), server).await { + Ok(Ok(Ok(()))) => {} + Ok(Ok(Err(error))) => { + tracing::error!(%error, "desktop server shutdown failed") + } + Ok(Err(error)) => { + tracing::error!(%error, "desktop server task failed") + } + Err(_) => tracing::warn!("desktop server shutdown timed out"), + } + } + app.exit(0); + }); } } - } - RunEvent::ExitRequested { api, .. } => { - let runtime = app.state::(); - if !runtime.exiting.swap(true, Ordering::AcqRel) { - api.prevent_exit(); - runtime.shutdown.cancel(); - let server = runtime.server.lock().expect("server lock poisoned").take(); - let app = app.clone(); - tauri::async_runtime::spawn(async move { - if let Some(server) = server { - match tokio::time::timeout(Duration::from_secs(11), server).await { - Ok(Ok(Ok(()))) => {} - Ok(Ok(Err(error))) => { - tracing::error!(%error, "desktop server shutdown failed") - } - Ok(Err(error)) => tracing::error!(%error, "desktop server task failed"), - Err(_) => tracing::warn!("desktop server shutdown timed out"), - } - } - app.exit(0); - }); - } - } + }, #[cfg(target_os = "macos")] RunEvent::Reopen { has_visible_windows: false, .. - } => tray::show_main_window(app), + } => { + let _ = open_main_window(app); + } _ => {} }); diff --git a/apps/desktop/src-tauri/src/tray.rs b/apps/desktop/src-tauri/src/tray.rs index a5a2d77..1e7c5df 100644 --- a/apps/desktop/src-tauri/src/tray.rs +++ b/apps/desktop/src-tauri/src/tray.rs @@ -1,13 +1,13 @@ use tauri::{ menu::{Menu, MenuItem, PredefinedMenuItem}, tray::TrayIconBuilder, - App, AppHandle, Manager, + App, }; #[cfg(target_os = "windows")] use tauri::tray::{MouseButton, MouseButtonState, TrayIconEvent}; -use crate::desktop::MAIN_WINDOW_LABEL; +use crate::desktop::open_main_window; const OPEN_MENU_ID: &str = "tray-open"; const QUIT_MENU_ID: &str = "tray-quit"; @@ -24,7 +24,9 @@ pub fn create(app: &mut App) -> tauri::Result<()> { .menu(&menu) .show_menu_on_left_click(cfg!(target_os = "macos")) .on_menu_event(|app, event| match event.id().as_ref() { - OPEN_MENU_ID => show_main_window(app), + OPEN_MENU_ID => { + let _ = open_main_window(app); + } QUIT_MENU_ID => app.exit(0), _ => {} }) @@ -36,7 +38,7 @@ pub fn create(app: &mut App) -> tauri::Result<()> { .. } = event { - show_main_window(tray.app_handle()); + let _ = open_main_window(tray.app_handle()); } #[cfg(not(target_os = "windows"))] @@ -45,11 +47,3 @@ pub fn create(app: &mut App) -> tauri::Result<()> { .build(app)?; Ok(()) } - -pub fn show_main_window(app: &AppHandle) { - if let Some(window) = app.get_webview_window(MAIN_WINDOW_LABEL) { - let _ = window.unminimize(); - let _ = window.show(); - let _ = window.set_focus(); - } -}