fix: resolve race condition freeze on rapid tray icon clicks in lightweight mode

This commit is contained in:
wonfen
2025-06-22 15:42:01 +08:00
parent 094feb74ec
commit a92872c831
9 changed files with 229 additions and 104 deletions

View File

@@ -1,10 +1,6 @@
use crate::{
config::Config,
core::handle,
feat, logging, logging_error,
module::lightweight::entry_lightweight_mode,
process::AsyncHandler,
utils::{logging::Type, resolve},
config::Config, core::handle, feat, logging, logging_error,
module::lightweight::entry_lightweight_mode, utils::logging::Type,
};
use anyhow::{bail, Result};
use once_cell::sync::OnceCell;
@@ -14,7 +10,7 @@ use tauri::Manager;
use tauri_plugin_global_shortcut::{Code, GlobalShortcutExt, ShortcutState};
pub struct Hotkey {
current: Arc<Mutex<Vec<String>>>, // 保存当前的热键设置
current: Arc<Mutex<Vec<String>>>,
}
impl Hotkey {
@@ -38,7 +34,6 @@ impl Hotkey {
enable_global_hotkey
);
// 如果全局热键被禁用,则不注册热键
if !enable_global_hotkey {
return Ok(());
}
@@ -153,76 +148,14 @@ impl Hotkey {
"=== Hotkey Dashboard Window Operation Start ==="
);
// 检查是否在轻量模式下,如果是,需要同步处理
if crate::module::lightweight::is_in_lightweight_mode() {
logging!(
info,
Type::Hotkey,
true,
"In lightweight mode, calling open_or_close_dashboard directly"
);
crate::feat::open_or_close_dashboard();
} else {
AsyncHandler::spawn(move || async move {
logging!(
debug,
Type::Hotkey,
true,
"Toggle dashboard window visibility (async)"
);
logging!(
info,
Type::Hotkey,
true,
"Using unified WindowManager for hotkey operation (bypass debounce)"
);
// 检查窗口是否存在
if let Some(window) = handle::Handle::global().get_window() {
// 如果窗口可见,则隐藏
match window.is_visible() {
Ok(visible) => {
if visible {
logging!(
info,
Type::Window,
true,
"Window is visible, hiding it"
);
let _ = window.hide();
} else {
// 如果窗口不可见,则显示
logging!(
info,
Type::Window,
true,
"Window is hidden, showing it"
);
if window.is_minimized().unwrap_or(false) {
let _ = window.unminimize();
}
let _ = window.show();
let _ = window.set_focus();
}
}
Err(e) => {
logging!(
warn,
Type::Window,
true,
"Failed to check window visibility: {}",
e
);
let _ = window.show();
let _ = window.set_focus();
}
}
} else {
// 如果窗口不存在,创建一个新窗口
logging!(
info,
Type::Window,
true,
"Window does not exist, creating a new one"
);
resolve::create_window(true);
}
});
}
crate::feat::open_or_close_dashboard_hotkey();
logging!(
debug,
@@ -261,10 +194,8 @@ impl Hotkey {
}
}
} else {
// 直接执行函数,不做任何状态检查
logging!(debug, Type::Hotkey, "Executing function directly");
// 获取全局热键状态
let is_enable_global_hotkey = Config::verge()
.latest()
.enable_global_hotkey
@@ -274,7 +205,6 @@ impl Hotkey {
f();
} else {
use crate::utils::window_manager::WindowManager;
// 非轻量模式且未启用全局热键时,只在窗口可见且有焦点的情况下响应热键
let is_visible = WindowManager::is_main_window_visible();
let is_focused = WindowManager::is_main_window_focused();

View File

@@ -39,6 +39,29 @@ use super::handle;
#[derive(Clone)]
struct TrayState {}
// 托盘点击防抖机制
static TRAY_CLICK_DEBOUNCE: OnceCell<Mutex<Instant>> = OnceCell::new();
const TRAY_CLICK_DEBOUNCE_MS: u64 = 300;
fn get_tray_click_debounce() -> &'static Mutex<Instant> {
TRAY_CLICK_DEBOUNCE.get_or_init(|| Mutex::new(Instant::now() - Duration::from_secs(1)))
}
fn should_handle_tray_click() -> bool {
let debounce_lock = get_tray_click_debounce();
let mut last_click = debounce_lock.lock();
let now = Instant::now();
if now.duration_since(*last_click) >= Duration::from_millis(TRAY_CLICK_DEBOUNCE_MS) {
*last_click = now;
true
} else {
log::debug!(target: "app", "托盘点击被防抖机制忽略,距离上次点击 {:?}ms",
now.duration_since(*last_click).as_millis());
false
}
}
#[cfg(target_os = "macos")]
pub struct Tray {
pub speed_rate: Arc<Mutex<Option<SpeedRate>>>,
@@ -664,6 +687,11 @@ impl Tray {
..
} = event
{
// 添加防抖检查,防止快速连击
if !should_handle_tray_click() {
return;
}
match tray_event.as_str() {
"system_proxy" => feat::toggle_system_proxy(),
"tun_mode" => feat::toggle_tun_mode(None),
@@ -949,12 +977,15 @@ fn on_menu_event(_: &AppHandle, event: MenuEvent) {
"open_window" => {
use crate::utils::window_manager::WindowManager;
log::info!(target: "app", "托盘菜单点击: 打开窗口");
// 如果在轻量模式中,先退出轻量模式
if !should_handle_tray_click() {
return;
}
if crate::module::lightweight::is_in_lightweight_mode() {
log::info!(target: "app", "当前在轻量模式,正在退出");
crate::module::lightweight::exit_lightweight_mode();
}
// 使用统一的窗口管理器显示窗口
let result = WindowManager::show_main_window();
log::info!(target: "app", "窗口显示结果: {:?}", result);
}
@@ -977,7 +1008,10 @@ fn on_menu_event(_: &AppHandle, event: MenuEvent) {
"restart_clash" => feat::restart_clash_core(),
"restart_app" => feat::restart_app(),
"entry_lightweight_mode" => {
// 处理轻量模式的切换
if !should_handle_tray_click() {
return;
}
let was_lightweight = crate::module::lightweight::is_in_lightweight_mode();
if was_lightweight {
crate::module::lightweight::exit_lightweight_mode();
@@ -985,7 +1019,6 @@ fn on_menu_event(_: &AppHandle, event: MenuEvent) {
crate::module::lightweight::entry_lightweight_mode();
}
// 退出轻量模式后显示主窗口
if was_lightweight {
use crate::utils::window_manager::WindowManager;
let result = WindowManager::show_main_window();
@@ -1002,7 +1035,6 @@ fn on_menu_event(_: &AppHandle, event: MenuEvent) {
_ => {}
}
// 统一调用状态更新
if let Err(e) = Tray::global().update_all_states() {
log::warn!(target: "app", "更新托盘状态失败: {}", e);
}