fix: resolve lightweight mode recovery failure caused by white screen prevention

This commit is contained in:
wonfen 2025-04-20 23:47:09 +08:00
parent abe5cf1b84
commit d05952e945
7 changed files with 152 additions and 85 deletions

View File

@ -222,3 +222,11 @@ pub fn notify_ui_ready() -> CmdResult<()> {
crate::utils::resolve::mark_ui_ready(); crate::utils::resolve::mark_ui_ready();
Ok(()) Ok(())
} }
/// 重置UI就绪状态
#[tauri::command]
pub fn reset_ui_ready_state() -> CmdResult<()> {
log::info!(target: "app", "重置UI就绪状态");
crate::utils::resolve::reset_ui_ready();
Ok(())
}

View File

@ -7,3 +7,9 @@ pub async fn entry_lightweight_mode() -> CmdResult {
lightweight::entry_lightweight_mode(); lightweight::entry_lightweight_mode();
Ok(()) Ok(())
} }
#[tauri::command]
pub async fn exit_lightweight_mode() -> CmdResult {
lightweight::exit_lightweight_mode();
Ok(())
}

View File

@ -206,7 +206,14 @@ impl Tray {
match tray_event.as_str() { match tray_event.as_str() {
"system_proxy" => feat::toggle_system_proxy(), "system_proxy" => feat::toggle_system_proxy(),
"tun_mode" => feat::toggle_tun_mode(None), "tun_mode" => feat::toggle_tun_mode(None),
"main_window" => resolve::create_window(true), "main_window" => {
// 如果在轻量模式中,先退出轻量模式
if crate::module::lightweight::is_in_lightweight_mode() {
crate::module::lightweight::exit_lightweight_mode();
}
// 然后创建窗口
resolve::create_window(true)
}
_ => {} _ => {}
} }
} }
@ -680,7 +687,14 @@ fn on_menu_event(_: &AppHandle, event: MenuEvent) {
println!("change mode to: {}", mode); println!("change mode to: {}", mode);
feat::change_clash_mode(mode.into()); feat::change_clash_mode(mode.into());
} }
"open_window" => resolve::create_window(true), "open_window" => {
// 如果在轻量模式中,先退出轻量模式
if crate::module::lightweight::is_in_lightweight_mode() {
crate::module::lightweight::exit_lightweight_mode();
}
// 然后创建窗口
resolve::create_window(true)
}
"system_proxy" => feat::toggle_system_proxy(), "system_proxy" => feat::toggle_system_proxy(),
"tun_mode" => feat::toggle_tun_mode(None), "tun_mode" => feat::toggle_tun_mode(None),
"copy_env" => feat::copy_clash_env(), "copy_env" => feat::copy_clash_env(),

View File

@ -160,10 +160,14 @@ pub fn run() {
cmd::restart_app, cmd::restart_app,
// 添加新的命令 // 添加新的命令
cmd::notify_ui_ready, cmd::notify_ui_ready,
cmd::reset_ui_ready_state,
cmd::get_running_mode, cmd::get_running_mode,
cmd::get_app_uptime, cmd::get_app_uptime,
cmd::get_auto_launch_status, cmd::get_auto_launch_status,
cmd::is_admin, cmd::is_admin,
// 添加轻量模式相关命令
cmd::entry_lightweight_mode,
cmd::exit_lightweight_mode,
// service 管理 // service 管理
cmd::install_service, cmd::install_service,
cmd::uninstall_service, cmd::uninstall_service,

View File

@ -9,12 +9,33 @@ use crate::{
use anyhow::{Context, Result}; use anyhow::{Context, Result};
use delay_timer::prelude::TaskBuilder; use delay_timer::prelude::TaskBuilder;
use once_cell::sync::OnceCell; use once_cell::sync::OnceCell;
use parking_lot::RwLock;
use std::sync::Arc;
use tauri::{Listener, Manager}; use tauri::{Listener, Manager};
pub static AUTO_LIGHT_WEIGHT_MODE_INIT: OnceCell<()> = OnceCell::new(); pub static AUTO_LIGHT_WEIGHT_MODE_INIT: OnceCell<()> = OnceCell::new();
const LIGHT_WEIGHT_TASK_UID: &str = "light_weight_task"; const LIGHT_WEIGHT_TASK_UID: &str = "light_weight_task";
// 轻量模式状态标志
static IS_LIGHTWEIGHT_MODE: OnceCell<Arc<RwLock<bool>>> = OnceCell::new();
fn get_lightweight_mode() -> &'static Arc<RwLock<bool>> {
IS_LIGHTWEIGHT_MODE.get_or_init(|| Arc::new(RwLock::new(false)))
}
// 检查是否处于轻量模式
pub fn is_in_lightweight_mode() -> bool {
*get_lightweight_mode().read()
}
// 设置轻量模式状态
fn set_lightweight_mode(value: bool) {
let mut mode = get_lightweight_mode().write();
*mode = value;
logging!(info, Type::Lightweight, true, "轻量模式状态: {}", value);
}
pub fn enable_auto_light_weight_mode() { pub fn enable_auto_light_weight_mode() {
Timer::global().init().unwrap(); Timer::global().init().unwrap();
logging!(info, Type::Lightweight, true, "开启自动轻量模式"); logging!(info, Type::Lightweight, true, "开启自动轻量模式");
@ -40,9 +61,21 @@ pub fn entry_lightweight_mode() {
AppHandleManager::global().set_activation_policy_accessory(); AppHandleManager::global().set_activation_policy_accessory();
logging!(info, Type::Lightweight, true, "轻量模式已开启"); logging!(info, Type::Lightweight, true, "轻量模式已开启");
} }
// 标记已进入轻量模式
set_lightweight_mode(true);
let _ = cancel_light_weight_timer(); let _ = cancel_light_weight_timer();
} }
// 从轻量模式恢复
pub fn exit_lightweight_mode() {
// 标记退出轻量模式
set_lightweight_mode(false);
logging!(info, Type::Lightweight, true, "正在退出轻量模式");
// 重置UI就绪状态
crate::utils::resolve::reset_ui_ready();
}
pub fn add_light_weight_timer() { pub fn add_light_weight_timer() {
logging_error!(Type::Lightweight, setup_light_weight_timer()); logging_error!(Type::Lightweight, setup_light_weight_timer());
} }

View File

@ -16,7 +16,7 @@ use percent_encoding::percent_decode_str;
use serde_json; use serde_json;
use serde_yaml::Mapping; use serde_yaml::Mapping;
use std::{net::TcpListener, sync::Arc}; use std::{net::TcpListener, sync::Arc};
use tauri::{App, Manager}; use tauri::{App, Emitter, Manager};
use tauri::Url; use tauri::Url;
//#[cfg(not(target_os = "linux"))] //#[cfg(not(target_os = "linux"))]
@ -41,6 +41,13 @@ pub fn mark_ui_ready() {
*ready = true; *ready = true;
} }
// 重置UI就绪状态
pub fn reset_ui_ready() {
let mut ready = get_ui_ready().write();
*ready = false;
logging!(info, Type::Window, true, "UI就绪状态已重置");
}
pub fn find_unused_port() -> Result<u16> { pub fn find_unused_port() -> Result<u16> {
match TcpListener::bind("127.0.0.1:0") { match TcpListener::bind("127.0.0.1:0") {
Ok(listener) => { Ok(listener) => {
@ -219,26 +226,30 @@ pub fn create_window(is_showup: bool) {
#[cfg(target_os = "macos")] #[cfg(target_os = "macos")]
AppHandleManager::global().set_activation_policy_regular(); AppHandleManager::global().set_activation_policy_regular();
// 检查是否从轻量模式恢复
let from_lightweight = crate::module::lightweight::is_in_lightweight_mode();
if from_lightweight {
logging!(info, Type::Window, true, "从轻量模式恢复窗口");
crate::module::lightweight::exit_lightweight_mode();
}
if let Some(window) = handle::Handle::global().get_window() { if let Some(window) = handle::Handle::global().get_window() {
logging!( logging!(info, Type::Window, true, "Found existing window");
info,
Type::Window,
true,
"Found existing window, attempting to restore visibility"
);
if window.is_minimized().unwrap_or(false) { if window.is_minimized().unwrap_or(false) {
logging!(
info,
Type::Window,
true,
"Window is minimized, restoring window state"
);
let _ = window.unminimize(); let _ = window.unminimize();
} }
let _ = window.show();
let _ = window.set_focus(); if from_lightweight {
return; // 从轻量模式恢复需要销毁旧窗口以重建
logging!(info, Type::Window, true, "销毁旧窗口以重建新窗口");
let _ = window.close();
} else {
// 普通情况直接显示窗口
let _ = window.show();
let _ = window.set_focus();
return;
}
} }
// 定义默认窗口大小 // 定义默认窗口大小
@ -346,83 +357,65 @@ pub fn create_window(is_showup: bool) {
} }
} }
// 创建异步任务处理UI就绪和显示窗口
let was_from_lightweight = from_lightweight;
AsyncHandler::spawn(move || async move { AsyncHandler::spawn(move || async move {
use tauri::Emitter; // 处理启动完成
logging!(info, Type::Window, true, "UI gets ready.");
handle::Handle::global().mark_startup_completed(); handle::Handle::global().mark_startup_completed();
if let Some(window) = app_handle_clone.get_webview_window("main") { if let Some(window) = app_handle_clone.get_webview_window("main") {
// 检查窗口大小
match window.inner_size() {
Ok(size) => {
let width = size.width;
let height = size.height;
let state_width = STATE_WIDTH.get().copied().unwrap_or(DEFAULT_WIDTH);
let state_height =
STATE_HEIGHT.get().copied().unwrap_or(DEFAULT_HEIGHT);
logging!(
info,
Type::Window,
true,
"异步任务中窗口尺寸: {}x{}, 状态文件尺寸: {}x{}",
width,
height,
state_width,
state_height
);
}
Err(e) => {
logging!(
error,
Type::Window,
true,
"Failed to get window size: {:?}",
e
);
}
}
// 发送启动完成事件 // 发送启动完成事件
let _ = window.emit("verge://startup-completed", ()); let _ = window.emit("verge://startup-completed", ());
if is_showup { if is_showup {
// 启动一个任务等待UI准备就绪再显示窗口
let window_clone = window.clone(); let window_clone = window.clone();
AsyncHandler::spawn(move || async move {
async fn wait_for_ui_ready() {
while !*get_ui_ready().read() {
tokio::time::sleep(std::time::Duration::from_millis(50)).await;
}
}
match tokio::time::timeout( // 从轻量模式恢复时使用较短的超时,避免卡死
std::time::Duration::from_secs(5), let timeout_seconds = if was_from_lightweight {
wait_for_ui_ready(), // 从轻量模式恢复只等待2秒确保不会卡死
) 2
.await } else {
{ 5
Ok(_) => { };
logging!(info, Type::Window, true, "UI准备就绪显示窗口");
}
Err(_) => {
logging!(
warn,
Type::Window,
true,
"等待UI准备就绪超时强制显示窗口"
);
}
}
let _ = window_clone.show(); // 等待UI就绪
let _ = window_clone.set_focus(); async fn wait_for_ui_ready() {
}); while !*get_ui_ready().read() {
tokio::time::sleep(std::time::Duration::from_millis(100)).await;
}
}
// 使用超时机制等待UI就绪
match tokio::time::timeout(
std::time::Duration::from_secs(timeout_seconds),
wait_for_ui_ready(),
)
.await
{
Ok(_) => {
logging!(info, Type::Window, true, "UI准备就绪显示窗口");
}
Err(_) => {
logging!(
warn,
Type::Window,
true,
"等待UI准备就绪超时{}秒),强制显示窗口",
timeout_seconds
);
}
}
// 无论是否超时,都显示窗口
let _ = window_clone.show();
let _ = window_clone.set_focus();
} }
} else {
logging!(error, Type::Window, true, "无法获取主窗口");
} }
}); });
logging!(info, Type::Window, true, "异步任务已创建");
} }
Err(e) => { Err(e) => {
logging!( logging!(

View File

@ -214,6 +214,8 @@ const Layout = () => {
useEffect(() => { useEffect(() => {
const notifyUiReady = async () => { const notifyUiReady = async () => {
try { try {
await invoke("reset_ui_ready_state");
console.log("已重置UI就绪状态");
await new Promise(resolve => setTimeout(resolve, 200)); await new Promise(resolve => setTimeout(resolve, 200));
await invoke("notify_ui_ready"); await invoke("notify_ui_ready");
console.log("已通知后端UI准备就绪"); console.log("已通知后端UI准备就绪");
@ -224,12 +226,19 @@ const Layout = () => {
// 监听后端发送的启动完成事件 // 监听后端发送的启动完成事件
const listenStartupCompleted = async () => { const listenStartupCompleted = async () => {
const unlisten = await listen("verge://startup-completed", () => { try {
console.log("收到启动完成事件"); const unlisten = await listen("verge://startup-completed", () => {
}); console.log("收到启动完成事件开始通知UI就绪");
return unlisten; notifyUiReady();
});
return unlisten;
} catch (err) {
console.error("监听启动完成事件失败:", err);
return () => {};
}
}; };
console.log("页面初始加载通知UI就绪");
notifyUiReady(); notifyUiReady();
const unlistenPromise = listenStartupCompleted(); const unlistenPromise = listenStartupCompleted();