refactor: improve hotkey management, logging, and error handling; fix tray freeze and hotkey failure on silent startup

This commit is contained in:
wonfen 2025-02-12 14:23:42 +08:00
parent c98330ea1f
commit 3bb3872e38
3 changed files with 139 additions and 35 deletions

View File

@ -1,11 +1,14 @@
use crate::core::handle; use crate::core::handle;
use crate::{config::Config, feat, log_err}; use crate::{config::Config, feat, log_err};
use crate::utils::resolve;
use anyhow::{bail, Result}; use anyhow::{bail, Result};
use once_cell::sync::OnceCell; use once_cell::sync::OnceCell;
use parking_lot::Mutex; use parking_lot::Mutex;
use std::{collections::HashMap, sync::Arc}; use std::{collections::HashMap, sync::Arc};
use tauri::Manager; use tauri::Manager;
use tauri_plugin_global_shortcut::{Code, GlobalShortcutExt, ShortcutState}; use tauri_plugin_global_shortcut::{Code, GlobalShortcutExt, ShortcutState};
use tauri::async_runtime;
pub struct Hotkey { pub struct Hotkey {
current: Arc<Mutex<Vec<String>>>, // 保存当前的热键设置 current: Arc<Mutex<Vec<String>>>, // 保存当前的热键设置
} }
@ -21,8 +24,22 @@ impl Hotkey {
pub fn init(&self) -> Result<()> { pub fn init(&self) -> Result<()> {
let verge = Config::verge(); let verge = Config::verge();
let enable_global_hotkey = verge.latest().enable_global_hotkey.unwrap_or(true);
println!("Initializing hotkeys, global hotkey enabled: {}", enable_global_hotkey);
log::info!(target: "app", "Initializing hotkeys, global hotkey enabled: {}", enable_global_hotkey);
// 如果全局热键被禁用,则不注册热键
if !enable_global_hotkey {
println!("Global hotkey is disabled, skipping registration");
log::info!(target: "app", "Global hotkey is disabled, skipping registration");
return Ok(());
}
if let Some(hotkeys) = verge.latest().hotkeys.as_ref() { if let Some(hotkeys) = verge.latest().hotkeys.as_ref() {
println!("Found {} hotkeys to register", hotkeys.len());
log::info!(target: "app", "Found {} hotkeys to register", hotkeys.len());
for hotkey in hotkeys.iter() { for hotkey in hotkeys.iter() {
let mut iter = hotkey.split(','); let mut iter = hotkey.split(',');
let func = iter.next(); let func = iter.next();
@ -30,16 +47,28 @@ impl Hotkey {
match (key, func) { match (key, func) {
(Some(key), Some(func)) => { (Some(key), Some(func)) => {
log_err!(self.register(key, func)); println!("Registering hotkey: {} -> {}", key, func);
log::info!(target: "app", "Registering hotkey: {} -> {}", key, func);
if let Err(e) = self.register(key, func) {
println!("Failed to register hotkey {} -> {}: {:?}", key, func, e);
log::error!(target: "app", "Failed to register hotkey {} -> {}: {:?}", key, func, e);
} else {
println!("Successfully registered hotkey {} -> {}", key, func);
log::info!(target: "app", "Successfully registered hotkey {} -> {}", key, func);
}
} }
_ => { _ => {
let key = key.unwrap_or("None"); let key = key.unwrap_or("None");
let func = func.unwrap_or("None"); let func = func.unwrap_or("None");
log::error!(target: "app", "invalid hotkey `{key}`:`{func}`"); println!("Invalid hotkey configuration: `{key}`:`{func}`");
log::error!(target: "app", "Invalid hotkey configuration: `{key}`:`{func}`");
} }
} }
} }
self.current.lock().clone_from(hotkeys); self.current.lock().clone_from(hotkeys);
} else {
println!("No hotkeys configured");
log::info!(target: "app", "No hotkeys configured");
} }
Ok(()) Ok(())
@ -56,49 +85,74 @@ impl Hotkey {
let app_handle = handle::Handle::global().app_handle().unwrap(); let app_handle = handle::Handle::global().app_handle().unwrap();
let manager = app_handle.global_shortcut(); let manager = app_handle.global_shortcut();
println!("Attempting to register hotkey: {} for function: {}", hotkey, func);
log::info!(target: "app", "Attempting to register hotkey: {} for function: {}", hotkey, func);
if manager.is_registered(hotkey) { if manager.is_registered(hotkey) {
println!("Hotkey {} was already registered, unregistering first", hotkey);
log::info!(target: "app", "Hotkey {} was already registered, unregistering first", hotkey);
manager.unregister(hotkey)?; manager.unregister(hotkey)?;
} }
let f = match func.trim() { let f = match func.trim() {
"open_or_close_dashboard" => feat::open_or_close_dashboard, "open_or_close_dashboard" => {
println!("Registering open_or_close_dashboard function");
log::info!(target: "app", "Registering open_or_close_dashboard function");
|| {
println!("=== Hotkey Dashboard Window Operation Start ===");
log::info!(target: "app", "=== Hotkey Dashboard Window Operation Start ===");
// 使用 spawn_blocking 来确保在正确的线程上执行
async_runtime::spawn_blocking(|| {
println!("Creating window in spawn_blocking");
log::info!(target: "app", "Creating window in spawn_blocking");
resolve::create_window();
});
println!("=== Hotkey Dashboard Window Operation End ===");
log::info!(target: "app", "=== Hotkey Dashboard Window Operation End ===");
}
},
"clash_mode_rule" => || feat::change_clash_mode("rule".into()), "clash_mode_rule" => || feat::change_clash_mode("rule".into()),
"clash_mode_global" => || feat::change_clash_mode("global".into()), "clash_mode_global" => || feat::change_clash_mode("global".into()),
"clash_mode_direct" => || feat::change_clash_mode("direct".into()), "clash_mode_direct" => || feat::change_clash_mode("direct".into()),
"toggle_system_proxy" => feat::toggle_system_proxy, "toggle_system_proxy" => || feat::toggle_system_proxy(),
"toggle_tun_mode" => feat::toggle_tun_mode, "toggle_tun_mode" => || feat::toggle_tun_mode(),
"quit" => || feat::quit(Some(0)), "quit" => || feat::quit(Some(0)),
_ => bail!("invalid function \"{func}\""), _ => {
println!("Invalid function: {}", func);
log::error!(target: "app", "Invalid function: {}", func);
bail!("invalid function \"{func}\"");
}
}; };
let is_quit = func.trim() == "quit"; let is_quit = func.trim() == "quit";
let _ = manager.on_shortcut(hotkey, move |app_handle, hotkey, event| { let _ = manager.on_shortcut(hotkey, move |app_handle, hotkey, event| {
if event.state == ShortcutState::Pressed { if event.state == ShortcutState::Pressed {
println!("Hotkey pressed: {:?}", hotkey);
log::info!(target: "app", "Hotkey pressed: {:?}", hotkey);
if hotkey.key == Code::KeyQ && is_quit { if hotkey.key == Code::KeyQ && is_quit {
if let Some(window) = app_handle.get_webview_window("main") { if let Some(window) = app_handle.get_webview_window("main") {
if window.is_focused().unwrap_or(false) { if window.is_focused().unwrap_or(false) {
println!("Executing quit function");
log::info!(target: "app", "Executing quit function");
f(); f();
} }
} }
} else { } else {
if let Some(window) = app_handle.get_webview_window("main") { // 直接执行函数,不做任何状态检查
let is_enable_global_hotkey = Config::verge().latest().enable_global_hotkey.unwrap_or(true); println!("Executing function directly");
let is_visible = window.is_visible().unwrap_or(false); log::info!(target: "app", "Executing function directly");
let is_focused = window.is_focused().unwrap_or(false);
if is_enable_global_hotkey {
f(); f();
} else if is_focused && is_visible {
f();
}
}
} }
} }
}); });
log::debug!(target: "app", "register hotkey {hotkey} {func}"); println!("Successfully registered hotkey {} for {}", hotkey, func);
log::info!(target: "app", "Successfully registered hotkey {} for {}", hotkey, func);
Ok(()) Ok(())
} }

View File

@ -20,15 +20,41 @@ use tauri_plugin_window_state::{AppHandleExt, StateFlags};
// 打开面板 // 打开面板
pub fn open_or_close_dashboard() { pub fn open_or_close_dashboard() {
println!("Attempting to open/close dashboard");
log::info!(target: "app", "Attempting to open/close dashboard");
if let Some(window) = handle::Handle::global().get_window() { if let Some(window) = handle::Handle::global().get_window() {
println!("Found existing window");
log::info!(target: "app", "Found existing window");
// 如果窗口存在,则切换其显示状态 // 如果窗口存在,则切换其显示状态
if window.is_visible().unwrap_or(false) { match window.is_visible() {
Ok(visible) => {
println!("Window visibility status: {}", visible);
log::info!(target: "app", "Window visibility status: {}", visible);
if visible {
println!("Attempting to hide window");
log::info!(target: "app", "Attempting to hide window");
let _ = window.hide(); let _ = window.hide();
} else { } else {
println!("Attempting to show and focus window");
log::info!(target: "app", "Attempting to show and focus window");
if window.is_minimized().unwrap_or(false) {
let _ = window.unminimize();
}
let _ = window.show(); let _ = window.show();
let _ = window.set_focus(); let _ = window.set_focus();
} }
}
Err(e) => {
println!("Failed to get window visibility: {:?}", e);
log::error!(target: "app", "Failed to get window visibility: {:?}", e);
}
}
} else { } else {
println!("No existing window found, creating new window");
log::info!(target: "app", "No existing window found, creating new window");
resolve::create_window(); resolve::create_window();
} }
} }

View File

@ -95,16 +95,19 @@ pub async fn resolve_setup(app: &mut App) {
log_err!(tray::Tray::global().init()); log_err!(tray::Tray::global().init());
log_err!(tray::Tray::global().create_systray()); log_err!(tray::Tray::global().create_systray());
log_err!(sysopt::Sysopt::global().update_sysproxy().await);
log_err!(sysopt::Sysopt::global().init_guard_sysproxy());
// 初始化热键
log::trace!(target: "app", "init hotkeys");
log_err!(hotkey::Hotkey::global().init());
let silent_start = { Config::verge().data().enable_silent_start }; let silent_start = { Config::verge().data().enable_silent_start };
if !silent_start.unwrap_or(false) { if !silent_start.unwrap_or(false) {
create_window(); create_window();
} }
log_err!(sysopt::Sysopt::global().update_sysproxy().await);
log_err!(sysopt::Sysopt::global().init_guard_sysproxy());
log_err!(tray::Tray::global().update_part()); log_err!(tray::Tray::global().update_part());
log_err!(hotkey::Hotkey::global().init());
log_err!(timer::Timer::global().init()); log_err!(timer::Timer::global().init());
} }
@ -123,10 +126,18 @@ pub fn resolve_reset() {
/// create main window /// create main window
pub fn create_window() { pub fn create_window() {
println!("Starting to create window");
log::info!(target: "app", "Starting to create window");
let app_handle = handle::Handle::global().app_handle().unwrap(); let app_handle = handle::Handle::global().app_handle().unwrap();
if let Some(window) = handle::Handle::global().get_window() { if let Some(window) = handle::Handle::global().get_window() {
println!("Found existing window, trying to show it");
log::info!(target: "app", "Found existing window, trying to show it");
if window.is_minimized().unwrap_or(false) { if window.is_minimized().unwrap_or(false) {
println!("Window is minimized, unminimizing");
log::info!(target: "app", "Window is minimized, unminimizing");
let _ = window.unminimize(); let _ = window.unminimize();
} }
let _ = window.show(); let _ = window.show();
@ -134,14 +145,16 @@ pub fn create_window() {
return; return;
} }
println!("Creating new window");
log::info!(target: "app", "Creating new window");
#[cfg(target_os = "windows")] #[cfg(target_os = "windows")]
let _ = tauri::WebviewWindowBuilder::new( let window = tauri::WebviewWindowBuilder::new(
&app_handle, &app_handle,
"main".to_string(), "main".to_string(),
tauri::WebviewUrl::App("index.html".into()), tauri::WebviewUrl::App("index.html".into()),
) )
.title("Clash Verge") .title("Clash Verge")
.visible(false)
.inner_size(890.0, 700.0) .inner_size(890.0, 700.0)
.min_inner_size(620.0, 550.0) .min_inner_size(620.0, 550.0)
.decorations(false) .decorations(false)
@ -152,7 +165,7 @@ pub fn create_window() {
.build(); .build();
#[cfg(target_os = "macos")] #[cfg(target_os = "macos")]
let _ = tauri::WebviewWindowBuilder::new( let window = tauri::WebviewWindowBuilder::new(
&app_handle, &app_handle,
"main".to_string(), "main".to_string(),
tauri::WebviewUrl::App("index.html".into()), tauri::WebviewUrl::App("index.html".into()),
@ -162,11 +175,10 @@ pub fn create_window() {
.title_bar_style(tauri::TitleBarStyle::Overlay) .title_bar_style(tauri::TitleBarStyle::Overlay)
.inner_size(890.0, 700.0) .inner_size(890.0, 700.0)
.min_inner_size(620.0, 550.0) .min_inner_size(620.0, 550.0)
.build() .build();
.unwrap();
#[cfg(target_os = "linux")] #[cfg(target_os = "linux")]
let _ = tauri::WebviewWindowBuilder::new( let window = tauri::WebviewWindowBuilder::new(
&app_handle, &app_handle,
"main".to_string(), "main".to_string(),
tauri::WebviewUrl::App("index.html".into()), tauri::WebviewUrl::App("index.html".into()),
@ -176,8 +188,20 @@ pub fn create_window() {
.inner_size(890.0, 700.0) .inner_size(890.0, 700.0)
.min_inner_size(620.0, 550.0) .min_inner_size(620.0, 550.0)
.transparent(true) .transparent(true)
.build() .build();
.unwrap();
match window {
Ok(window) => {
println!("Window created successfully, attempting to show");
log::info!(target: "app", "Window created successfully, attempting to show");
let _ = window.show();
let _ = window.set_focus();
}
Err(e) => {
println!("Failed to create window: {:?}", e);
log::error!(target: "app", "Failed to create window: {:?}", e);
}
}
} }
pub async fn resolve_scheme(param: String) -> Result<()> { pub async fn resolve_scheme(param: String) -> Result<()> {