feat: improve core functionality to prevent main process blocking, enhance MihomoManager, and optimize window creation process

This commit is contained in:
wonfen
2025-04-25 18:24:16 +08:00
parent d6a79316a6
commit 55cde38562
4 changed files with 256 additions and 155 deletions

View File

@@ -6,13 +6,14 @@ use crate::{
logging, logging_error,
module::lightweight,
process::AsyncHandler,
utils::{error, init, logging::Type, server},
utils::{dirs, error, init, logging::Type, server},
wrap_err,
};
use anyhow::{bail, Result};
use once_cell::sync::OnceCell;
use parking_lot::{Mutex, RwLock};
use percent_encoding::percent_decode_str;
use serde::{Deserialize, Serialize};
use serde_json;
use serde_yaml::Mapping;
use std::{
@@ -32,12 +33,23 @@ pub static VERSION: OnceCell<String> = OnceCell::new();
static STATE_WIDTH: OnceCell<u32> = OnceCell::new();
static STATE_HEIGHT: OnceCell<u32> = OnceCell::new();
// 定义默认窗口尺寸常量
const DEFAULT_WIDTH: u32 = 900;
const DEFAULT_HEIGHT: u32 = 700;
// 添加全局UI准备就绪标志
static UI_READY: OnceCell<Arc<RwLock<bool>>> = OnceCell::new();
// 窗口创建锁,防止并发创建窗口
static WINDOW_CREATING: OnceCell<Mutex<(bool, Instant)>> = OnceCell::new();
// 定义窗口状态结构体
#[derive(Debug, Serialize, Deserialize)]
struct WindowState {
width: Option<u32>,
height: Option<u32>,
}
fn get_window_creating_lock() -> &'static Mutex<(bool, Instant)> {
WINDOW_CREATING.get_or_init(|| Mutex::new((false, Instant::now())))
}
@@ -195,49 +207,47 @@ pub fn create_window(is_showup: bool) {
let _guard = WindowCreateGuard;
// 打印 .window-state.json 文件路径
if let Ok(app_dir) = crate::utils::dirs::app_home_dir() {
let window_state_path = app_dir.join(".window-state.json");
logging!(
info,
Type::Window,
true,
"窗口状态文件路径: {:?}",
window_state_path
);
let window_state_file = dirs::app_home_dir()
.ok()
.map(|dir| dir.join(".window-state.json"));
logging!(
info,
Type::Window,
true,
"窗口状态文件路径: {:?}",
window_state_file
);
// 尝试读取窗口状态文件内容
if window_state_path.exists() {
match std::fs::read_to_string(&window_state_path) {
// 从文件加载窗口状态
if let Some(window_state_file_path) = window_state_file {
if window_state_file_path.exists() {
match std::fs::read_to_string(&window_state_file_path) {
Ok(content) => {
logging!(info, Type::Window, true, "窗口状态文件内容: {}", content);
logging!(
debug,
Type::Window,
true,
"读取窗口状态文件内容成功: {} 字节",
content.len()
);
// 解析窗口状态文件
match serde_json::from_str::<serde_json::Value>(&content) {
Ok(state_json) => {
if let Some(main_window) = state_json.get("main") {
let width = main_window
.get("width")
.and_then(|v| v.as_u64())
.unwrap_or(0)
as u32;
let height = main_window
.get("height")
.and_then(|v| v.as_u64())
.unwrap_or(0)
as u32;
match serde_json::from_str::<WindowState>(&content) {
Ok(window_state) => {
logging!(
info,
Type::Window,
true,
"成功解析窗口状态: width={:?}, height={:?}",
window_state.width,
window_state.height
);
logging!(
info,
Type::Window,
true,
"窗口状态文件中的尺寸: {}x{}",
width,
height
);
// 保存读取到的尺寸,用于后续检查
STATE_WIDTH.get_or_init(|| width);
STATE_HEIGHT.get_or_init(|| height);
// 存储窗口状态以供后续使用
if let Some(width) = window_state.width {
STATE_WIDTH.set(width).ok();
}
if let Some(height) = window_state.height {
STATE_HEIGHT.set(height).ok();
}
}
Err(e) => {
@@ -299,56 +309,59 @@ pub fn create_window(is_showup: bool) {
}
}
// 定义默认窗口大小
const DEFAULT_WIDTH: u32 = 900;
const DEFAULT_HEIGHT: u32 = 700;
const MIN_WIDTH: u32 = 650;
const MIN_HEIGHT: u32 = 580;
let width = STATE_WIDTH.get().copied().unwrap_or(DEFAULT_WIDTH);
let height = STATE_HEIGHT.get().copied().unwrap_or(DEFAULT_HEIGHT);
#[cfg(target_os = "windows")]
let window = tauri::WebviewWindowBuilder::new(
&app_handle,
"main".to_string(),
tauri::WebviewUrl::App("index.html".into()),
)
.title("Clash Verge")
.inner_size(DEFAULT_WIDTH as f64, DEFAULT_HEIGHT as f64)
.min_inner_size(MIN_WIDTH as f64, MIN_HEIGHT as f64)
.decorations(false)
.maximizable(true)
.additional_browser_args("--enable-features=msWebView2EnableDraggableRegions --disable-features=OverscrollHistoryNavigation,msExperimentalScrolling")
.transparent(true)
.shadow(true)
.visible(false) // 初始不可见等待UI加载完成后再显示
.build();
logging!(
info,
Type::Window,
true,
"Initializing new window with size: {}x{}",
width,
height
);
// 根据不同平台创建不同配置的窗口
#[cfg(target_os = "macos")]
let window = tauri::WebviewWindowBuilder::new(
&app_handle,
"main".to_string(),
tauri::WebviewUrl::App("index.html".into()),
)
.decorations(true)
.hidden_title(true)
.title_bar_style(tauri::TitleBarStyle::Overlay)
.inner_size(DEFAULT_WIDTH as f64, DEFAULT_HEIGHT as f64)
.min_inner_size(MIN_WIDTH as f64, MIN_HEIGHT as f64)
.visible(false) // 初始不可见等待UI加载完成后再显示
.build();
let win_builder = {
// 基本配置
let builder = tauri::WebviewWindowBuilder::new(
&app_handle,
"main",
tauri::WebviewUrl::App("index.html".into()),
)
.title("Clash Verge")
.center()
.decorations(true)
.hidden_title(true) // 隐藏标题文本
.fullscreen(false)
.inner_size(width as f64, height as f64)
.min_inner_size(520.0, 520.0)
.visible(false);
#[cfg(target_os = "linux")]
let window = tauri::WebviewWindowBuilder::new(
// 尝试设置标题栏样式
// 注意根据Tauri版本不同此API可能有变化
// 如果编译出错,请注释掉下面这行
let builder = builder.title_bar_style(tauri::TitleBarStyle::Overlay);
builder
};
#[cfg(not(target_os = "macos"))]
let win_builder = tauri::WebviewWindowBuilder::new(
&app_handle,
"main".to_string(),
"main",
tauri::WebviewUrl::App("index.html".into()),
)
.title("Clash Verge")
.decorations(false)
.inner_size(DEFAULT_WIDTH as f64, DEFAULT_HEIGHT as f64)
.min_inner_size(MIN_WIDTH as f64, MIN_HEIGHT as f64)
.transparent(true)
.visible(false) // 初始不可见等待UI加载完成后再显示
.build();
.center()
.fullscreen(false)
.inner_size(width as f64, height as f64)
.min_inner_size(520.0, 520.0)
.visible(false)
.decorations(false);
let window = win_builder.build();
match window {
Ok(window) => {
@@ -379,33 +392,43 @@ pub fn create_window(is_showup: bool) {
DEFAULT_HEIGHT
);
if state_width < DEFAULT_WIDTH || state_height < DEFAULT_HEIGHT {
// 优化窗口大小设置
if size.width < state_width || size.height < state_height {
logging!(
info,
Type::Window,
true,
"状态文件窗口尺寸小于默认值,将使用默认尺寸: {}x{}",
DEFAULT_WIDTH,
DEFAULT_HEIGHT
"强制设置窗口尺寸: {}x{}",
state_width,
state_height
);
let _ = window.set_size(tauri::LogicalSize::new(
DEFAULT_WIDTH as f64,
DEFAULT_HEIGHT as f64,
));
} else if size.width != state_width || size.height != state_height {
// 如果API报告的尺寸与状态文件不一致记录日志
logging!(
warn,
Type::Window,
true,
"API报告的窗口尺寸与状态文件不一致"
);
// 尝试不同的方式设置窗口大小
let _ = window.set_size(tauri::PhysicalSize {
width: state_width,
height: state_height,
});
// 关键:等待短暂时间让窗口尺寸生效
std::thread::sleep(std::time::Duration::from_millis(50));
// 再次检查窗口尺寸
if let Ok(new_size) = window.inner_size() {
logging!(
info,
Type::Window,
true,
"设置后API报告的窗口尺寸: {}x{}",
new_size.width,
new_size.height
);
}
}
}
// 创建异步任务处理UI就绪和显示窗口
// 标记此窗口是否从轻量模式恢复
let was_from_lightweight = from_lightweight;
AsyncHandler::spawn(move || async move {
// 处理启动完成
handle::Handle::global().mark_startup_completed();
@@ -425,53 +448,44 @@ pub fn create_window(is_showup: bool) {
5
};
// 等待UI就绪
async fn wait_for_ui_ready() {
while !*get_ui_ready().read() {
tokio::time::sleep(std::time::Duration::from_millis(100)).await;
}
}
// 使用普通的等待方式替代事件监听,简化实现
let wait_result =
tokio::time::timeout(Duration::from_secs(timeout_seconds), async {
while !*get_ui_ready().read() {
tokio::time::sleep(Duration::from_millis(100)).await;
}
})
.await;
// 使用超时机制等待UI就绪
match tokio::time::timeout(
std::time::Duration::from_secs(timeout_seconds),
wait_for_ui_ready(),
)
.await
{
// 根据结果处理
match wait_result {
Ok(_) => {
logging!(info, Type::Window, true, "UI准备就绪,显示窗口");
logging!(info, Type::Window, true, "UI就绪显示窗口");
}
Err(_) => {
logging!(
warn,
Type::Window,
true,
"等待UI准备就绪超时{}秒,强制显示窗口",
"等待UI就绪超时({}秒),强制显示窗口",
timeout_seconds
);
// 强制设置UI就绪状态
*get_ui_ready().write() = true;
}
}
// 无论是否超时,都显示窗口
// 显示窗口
let _ = window_clone.show();
let _ = window_clone.set_focus();
logging!(info, Type::Window, true, "窗口创建和显示流程已完成");
}
} else {
logging!(error, Type::Window, true, "无法获取主窗口");
}
});
logging!(info, Type::Window, true, "异步任务已创建");
}
Err(e) => {
logging!(
error,
Type::Window,
true,
"Failed to create window: {:?}",
e
);
logging!(error, Type::Window, true, "Failed to create window: {}", e);
}
}
}