Files
clash-proxy/src-tauri/src/utils/window_manager.rs

383 lines
13 KiB
Rust
Raw Normal View History

use crate::{core::handle, logging, utils::logging::Type};
use tauri::{Manager, WebviewWindow, Wry};
use once_cell::sync::OnceCell;
use parking_lot::Mutex;
use scopeguard;
use std::{
sync::atomic::{AtomicBool, Ordering},
time::{Duration, Instant},
};
/// 窗口操作结果
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum WindowOperationResult {
/// 窗口已显示并获得焦点
Shown,
/// 窗口已隐藏
Hidden,
/// 创建了新窗口
Created,
/// 操作失败
Failed,
/// 无需操作
NoAction,
}
/// 窗口状态
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum WindowState {
/// 窗口可见且有焦点
VisibleFocused,
/// 窗口可见但无焦点
VisibleUnfocused,
/// 窗口最小化
Minimized,
/// 窗口隐藏
Hidden,
/// 窗口不存在
NotExist,
}
// 窗口操作防抖机制
static WINDOW_OPERATION_DEBOUNCE: OnceCell<Mutex<Instant>> = OnceCell::new();
static WINDOW_OPERATION_IN_PROGRESS: AtomicBool = AtomicBool::new(false);
const WINDOW_OPERATION_DEBOUNCE_MS: u64 = 500;
fn get_window_operation_debounce() -> &'static Mutex<Instant> {
WINDOW_OPERATION_DEBOUNCE.get_or_init(|| Mutex::new(Instant::now() - Duration::from_secs(1)))
}
fn should_handle_window_operation() -> bool {
if WINDOW_OPERATION_IN_PROGRESS.load(Ordering::Acquire) {
log::warn!(target: "app", "[防抖] 窗口操作已在进行中,跳过重复调用");
return false;
}
let debounce_lock = get_window_operation_debounce();
let mut last_operation = debounce_lock.lock();
let now = Instant::now();
let elapsed = now.duration_since(*last_operation);
log::debug!(target: "app", "[防抖] 检查窗口操作间隔: {}ms (需要>={}ms)",
elapsed.as_millis(), WINDOW_OPERATION_DEBOUNCE_MS);
if elapsed >= Duration::from_millis(WINDOW_OPERATION_DEBOUNCE_MS) {
*last_operation = now;
WINDOW_OPERATION_IN_PROGRESS.store(true, Ordering::Release);
log::info!(target: "app", "[防抖] 窗口操作被允许执行");
true
} else {
log::warn!(target: "app", "[防抖] 窗口操作被防抖机制忽略,距离上次操作 {}ms < {}ms",
elapsed.as_millis(), WINDOW_OPERATION_DEBOUNCE_MS);
false
}
}
fn finish_window_operation() {
WINDOW_OPERATION_IN_PROGRESS.store(false, Ordering::Release);
}
/// 统一的窗口管理器
pub struct WindowManager;
impl WindowManager {
pub fn get_main_window_state() -> WindowState {
match Self::get_main_window() {
Some(window) => {
let is_minimized = window.is_minimized().unwrap_or(false);
let is_visible = window.is_visible().unwrap_or(false);
let is_focused = window.is_focused().unwrap_or(false);
if is_minimized {
return WindowState::Minimized;
}
if !is_visible {
return WindowState::Hidden;
}
if is_focused {
WindowState::VisibleFocused
} else {
WindowState::VisibleUnfocused
}
}
None => WindowState::NotExist,
}
}
/// 获取主窗口实例
pub fn get_main_window() -> Option<WebviewWindow<Wry>> {
handle::Handle::global()
.app_handle()
.and_then(|app| app.get_webview_window("main"))
}
/// 智能显示主窗口
pub fn show_main_window() -> WindowOperationResult {
// 防抖检查
if !should_handle_window_operation() {
return WindowOperationResult::NoAction;
}
let _guard = scopeguard::guard((), |_| {
finish_window_operation();
});
logging!(info, Type::Window, true, "开始智能显示主窗口");
logging!(
debug,
Type::Window,
true,
"{}",
Self::get_window_status_info()
);
let current_state = Self::get_main_window_state();
match current_state {
WindowState::NotExist => {
logging!(info, Type::Window, true, "窗口不存在,创建新窗口");
if Self::create_new_window() {
logging!(info, Type::Window, true, "窗口创建成功");
std::thread::sleep(std::time::Duration::from_millis(100));
WindowOperationResult::Created
} else {
logging!(warn, Type::Window, true, "窗口创建失败");
WindowOperationResult::Failed
}
}
WindowState::VisibleFocused => {
logging!(info, Type::Window, true, "窗口已经可见且有焦点,无需操作");
WindowOperationResult::NoAction
}
WindowState::VisibleUnfocused | WindowState::Minimized | WindowState::Hidden => {
if let Some(window) = Self::get_main_window() {
let state_after_check = Self::get_main_window_state();
if state_after_check == WindowState::VisibleFocused {
logging!(
info,
Type::Window,
true,
"窗口在检查期间已变为可见和有焦点状态"
);
return WindowOperationResult::NoAction;
}
Self::activate_window(&window)
} else {
WindowOperationResult::Failed
}
}
}
}
/// 切换主窗口显示状态(显示/隐藏)
pub fn toggle_main_window() -> WindowOperationResult {
// 防抖检查
if !should_handle_window_operation() {
return WindowOperationResult::NoAction;
}
let _guard = scopeguard::guard((), |_| {
finish_window_operation();
});
logging!(info, Type::Window, true, "开始切换主窗口显示状态");
let current_state = Self::get_main_window_state();
logging!(
info,
Type::Window,
true,
"当前窗口状态: {:?} | 详细状态: {}",
current_state,
Self::get_window_status_info()
);
match current_state {
WindowState::NotExist => {
// 窗口不存在,创建新窗口
logging!(info, Type::Window, true, "窗口不存在,将创建新窗口");
// 由于已经有防抖保护,直接调用内部方法
if Self::create_new_window() {
WindowOperationResult::Created
} else {
WindowOperationResult::Failed
}
}
WindowState::VisibleFocused | WindowState::VisibleUnfocused => {
logging!(
info,
Type::Window,
true,
"窗口可见(焦点状态: {}),将隐藏窗口",
if current_state == WindowState::VisibleFocused {
"有焦点"
} else {
"无焦点"
}
);
if let Some(window) = Self::get_main_window() {
match window.hide() {
Ok(_) => {
logging!(info, Type::Window, true, "窗口已成功隐藏");
WindowOperationResult::Hidden
}
Err(e) => {
logging!(warn, Type::Window, true, "隐藏窗口失败: {}", e);
WindowOperationResult::Failed
}
}
} else {
logging!(warn, Type::Window, true, "无法获取窗口实例");
WindowOperationResult::Failed
}
}
WindowState::Minimized | WindowState::Hidden => {
logging!(
info,
Type::Window,
true,
"窗口存在但被隐藏或最小化,将激活窗口"
);
if let Some(window) = Self::get_main_window() {
Self::activate_window(&window)
} else {
logging!(warn, Type::Window, true, "无法获取窗口实例");
WindowOperationResult::Failed
}
}
}
}
/// 激活窗口(取消最小化、显示、设置焦点)
fn activate_window(window: &WebviewWindow<Wry>) -> WindowOperationResult {
logging!(info, Type::Window, true, "开始激活窗口");
let mut operations_successful = true;
// 1. 如果窗口最小化,先取消最小化
if window.is_minimized().unwrap_or(false) {
logging!(info, Type::Window, true, "窗口已最小化,正在取消最小化");
if let Err(e) = window.unminimize() {
logging!(warn, Type::Window, true, "取消最小化失败: {}", e);
operations_successful = false;
}
}
// 2. 显示窗口
if let Err(e) = window.show() {
logging!(warn, Type::Window, true, "显示窗口失败: {}", e);
operations_successful = false;
}
// 3. 设置焦点
if let Err(e) = window.set_focus() {
logging!(warn, Type::Window, true, "设置窗口焦点失败: {}", e);
operations_successful = false;
}
// 4. 平台特定的激活策略
#[cfg(target_os = "macos")]
{
logging!(info, Type::Window, true, "应用 macOS 特定的激活策略");
handle::Handle::global().set_activation_policy_regular();
}
#[cfg(target_os = "windows")]
{
// Windows 尝试额外的激活方法
if let Err(e) = window.set_always_on_top(true) {
logging!(
debug,
Type::Window,
true,
"设置置顶失败(非关键错误): {}",
e
);
}
// 立即取消置顶
if let Err(e) = window.set_always_on_top(false) {
logging!(
debug,
Type::Window,
true,
"取消置顶失败(非关键错误): {}",
e
);
}
}
if operations_successful {
logging!(info, Type::Window, true, "窗口激活成功");
WindowOperationResult::Shown
} else {
logging!(warn, Type::Window, true, "窗口激活部分失败");
WindowOperationResult::Failed
}
}
/// 隐藏主窗口
pub fn hide_main_window() -> WindowOperationResult {
logging!(info, Type::Window, true, "开始隐藏主窗口");
match Self::get_main_window() {
Some(window) => match window.hide() {
Ok(_) => {
logging!(info, Type::Window, true, "窗口已隐藏");
WindowOperationResult::Hidden
}
Err(e) => {
logging!(warn, Type::Window, true, "隐藏窗口失败: {}", e);
WindowOperationResult::Failed
}
},
None => {
logging!(info, Type::Window, true, "窗口不存在,无需隐藏");
WindowOperationResult::NoAction
}
}
}
/// 检查窗口是否可见
pub fn is_main_window_visible() -> bool {
Self::get_main_window()
.map(|window| window.is_visible().unwrap_or(false))
.unwrap_or(false)
}
/// 检查窗口是否有焦点
pub fn is_main_window_focused() -> bool {
Self::get_main_window()
.map(|window| window.is_focused().unwrap_or(false))
.unwrap_or(false)
}
/// 检查窗口是否最小化
pub fn is_main_window_minimized() -> bool {
Self::get_main_window()
.map(|window| window.is_minimized().unwrap_or(false))
.unwrap_or(false)
}
/// 创建新窗口,防抖避免重复调用
fn create_new_window() -> bool {
refactor(async): migrate from sync-blocking async execution to true async with unified AsyncHandler::spawn (#4502) * feat: replace all tokio::spawn with unified AsyncHandler::spawn - 🚀 Core Improvements: * Replace all tokio::spawn calls with AsyncHandler::spawn for unified Tauri async task management * Prioritize converting sync functions to async functions to reduce spawn usage * Use .await directly in async contexts instead of spawn - 🔧 Major Changes: * core/hotkey.rs: Use AsyncHandler::spawn for hotkey callback functions * module/lightweight.rs: Async lightweight mode switching * feat/window.rs: Convert window operation functions to async, use .await internally * feat/proxy.rs, feat/clash.rs: Async proxy and mode switching functions * lib.rs: Window focus handling with AsyncHandler::spawn * core/tray/mod.rs: Complete async tray event handling - ✨ Technical Advantages: * Unified task tracking and debugging capabilities (via tokio-trace feature) * Better error handling and task management * Consistency with Tauri runtime * Reduced async boundaries for better performance - 🧪 Verification: * Compilation successful with 0 errors, 0 warnings * Maintains complete original functionality * Optimized async execution flow * feat: complete tokio fs migration and replace tokio::spawn with AsyncHandler 🚀 Major achievements: - Migrate 8 core modules from std::fs to tokio::fs - Create 6 Send-safe wrapper functions using spawn_blocking pattern - Replace all tokio::spawn calls with AsyncHandler::spawn for unified async task management - Solve all 19 Send trait compilation errors through innovative spawn_blocking architecture 🔧 Core changes: - config/profiles.rs: Add profiles_*_safe functions to handle Send trait constraints - cmd/profile.rs: Update all Tauri commands to use Send-safe operations - config/prfitem.rs: Replace append_item calls with profiles_append_item_safe - utils/help.rs: Convert YAML operations to async (read_yaml, save_yaml) - Multiple modules: Replace tokio::task::spawn_blocking with AsyncHandler::spawn_blocking ✅ Technical innovations: - spawn_blocking wrapper pattern resolves parking_lot RwLock Send trait conflicts - Maintain parking_lot performance while achieving Tauri async command compatibility - Preserve backwards compatibility with gradual migration strategy 🎯 Results: - Zero compilation errors - Zero warnings - All async file operations working correctly - Complete Send trait compliance for Tauri commands * feat: refactor app handle and command functions to use async/await for improved performance * feat: update async handling in profiles and logging functions for improved error handling and performance * fix: update TRACE_MINI_SIZE constant to improve task logging threshold * fix(windows): convert service management functions to async for improved performance * fix: convert service management functions to async for improved responsiveness * fix(ubuntu): convert install and reinstall service functions to async for improved performance * fix(linux): convert uninstall_service function to async for improved performance * fix: convert uninstall_service call to async for improved performance * fix: convert file and directory creation calls to async for improved performance * fix: convert hotkey functions to async for improved responsiveness * chore: update UPDATELOG.md for v2.4.1 with major improvements and performance optimizations
2025-08-26 01:49:51 +08:00
use crate::process::AsyncHandler;
use crate::utils::resolve;
refactor(async): migrate from sync-blocking async execution to true async with unified AsyncHandler::spawn (#4502) * feat: replace all tokio::spawn with unified AsyncHandler::spawn - 🚀 Core Improvements: * Replace all tokio::spawn calls with AsyncHandler::spawn for unified Tauri async task management * Prioritize converting sync functions to async functions to reduce spawn usage * Use .await directly in async contexts instead of spawn - 🔧 Major Changes: * core/hotkey.rs: Use AsyncHandler::spawn for hotkey callback functions * module/lightweight.rs: Async lightweight mode switching * feat/window.rs: Convert window operation functions to async, use .await internally * feat/proxy.rs, feat/clash.rs: Async proxy and mode switching functions * lib.rs: Window focus handling with AsyncHandler::spawn * core/tray/mod.rs: Complete async tray event handling - ✨ Technical Advantages: * Unified task tracking and debugging capabilities (via tokio-trace feature) * Better error handling and task management * Consistency with Tauri runtime * Reduced async boundaries for better performance - 🧪 Verification: * Compilation successful with 0 errors, 0 warnings * Maintains complete original functionality * Optimized async execution flow * feat: complete tokio fs migration and replace tokio::spawn with AsyncHandler 🚀 Major achievements: - Migrate 8 core modules from std::fs to tokio::fs - Create 6 Send-safe wrapper functions using spawn_blocking pattern - Replace all tokio::spawn calls with AsyncHandler::spawn for unified async task management - Solve all 19 Send trait compilation errors through innovative spawn_blocking architecture 🔧 Core changes: - config/profiles.rs: Add profiles_*_safe functions to handle Send trait constraints - cmd/profile.rs: Update all Tauri commands to use Send-safe operations - config/prfitem.rs: Replace append_item calls with profiles_append_item_safe - utils/help.rs: Convert YAML operations to async (read_yaml, save_yaml) - Multiple modules: Replace tokio::task::spawn_blocking with AsyncHandler::spawn_blocking ✅ Technical innovations: - spawn_blocking wrapper pattern resolves parking_lot RwLock Send trait conflicts - Maintain parking_lot performance while achieving Tauri async command compatibility - Preserve backwards compatibility with gradual migration strategy 🎯 Results: - Zero compilation errors - Zero warnings - All async file operations working correctly - Complete Send trait compliance for Tauri commands * feat: refactor app handle and command functions to use async/await for improved performance * feat: update async handling in profiles and logging functions for improved error handling and performance * fix: update TRACE_MINI_SIZE constant to improve task logging threshold * fix(windows): convert service management functions to async for improved performance * fix: convert service management functions to async for improved responsiveness * fix(ubuntu): convert install and reinstall service functions to async for improved performance * fix(linux): convert uninstall_service function to async for improved performance * fix: convert uninstall_service call to async for improved performance * fix: convert file and directory creation calls to async for improved performance * fix: convert hotkey functions to async for improved responsiveness * chore: update UPDATELOG.md for v2.4.1 with major improvements and performance optimizations
2025-08-26 01:49:51 +08:00
// 使用 tokio runtime 阻塞调用 async 函数
refactor: enhance async initialization and streamline setup process (#4560) * feat: Implement DNS management for macOS - Added `set_public_dns` and `restore_public_dns` functions in `dns.rs` to manage system DNS settings. - Introduced `resolve` module to encapsulate DNS and scheme resolution functionalities. - Implemented `resolve_scheme` function in `scheme.rs` to handle deep links and profile imports. - Created UI readiness management in `ui.rs` to track and update UI loading states. - Developed window management logic in `window.rs` to handle window creation and visibility. - Added initial loading overlay script in `window_script.rs` for better user experience during startup. - Updated server handling in `server.rs` to integrate new resolve functionalities. - Refactored window creation calls in `window_manager.rs` to use the new window management logic. * refactor: streamline asynchronous handling in config and resolve setup * Revert "refactor: streamline asynchronous handling in config and resolve setup" This reverts commit 23d7dc86d5b87a3a34df2ae69c2caacef803ef81. * fix: optimize asynchronous memory handling * fix: enhance task logging by adding size check for special cases * refactor: enhance async initialization and streamline setup process * refactor: optimize async setup by consolidating initialization tasks * chore: update changelog for Mihomo(Meta) kernel upgrade to v1.19.13 * fix: improve startup phase initialization performance * refactor: optimize file read/write performance to reduce application wait time * refactor: simplify app instance exit logic and adjust system proxy guard initialization * refactor: change resolve_setup_async to synchronous execution for improved performance * refactor: update resolve_setup_async to accept AppHandle for improved initialization flow * refactor: remove unnecessary initialization of portable flag in run function * refactor: consolidate async initialization tasks into a single blocking call for improved execution flow * refactor: optimize resolve_setup_async by restructuring async tasks for improved concurrency * refactor: streamline resolve_setup_async and embed_server for improved async handling * refactor: separate synchronous and asynchronous setup functions for improved clarity * refactor: simplify async notification handling and remove redundant network manager initialization * refactor: enhance async handling in proxy request cache and window creation logic * refactor: improve code formatting and readability in ProxyRequestCache * refactor: adjust singleton check timeout and optimize trace size conditions * refactor: update TRACE_SPECIAL_SIZE to include additional size condition * refactor: update kode-bridge dependency to version 0.2.1-rc2 * refactor: replace RwLock with AtomicBool for UI readiness and implement event-driven monitoring * refactor: convert async functions to synchronous for window management * Update src-tauri/src/utils/resolve/window.rs * fix: handle missing app_handle in create_window function * Update src-tauri/src/module/lightweight.rs
2025-08-29 23:51:09 +08:00
AsyncHandler::block_on(resolve::window::create_window(true))
}
/// 获取详细的窗口状态信息
pub fn get_window_status_info() -> String {
let state = Self::get_main_window_state();
let is_visible = Self::is_main_window_visible();
let is_focused = Self::is_main_window_focused();
let is_minimized = Self::is_main_window_minimized();
format!(
"窗口状态: {state:?} | 可见: {is_visible} | 有焦点: {is_focused} | 最小化: {is_minimized}"
)
}
}