mirror of
https://github.com/clash-verge-rev/clash-verge-rev.git
synced 2026-04-13 05:20:28 +08:00
refactor: reduce webview lock contention (#6271)
* refactor: replace handle::Handle::get_window() with WindowManager::get_main_window() in multiple files * refactor: enhance WindowManager to return window state alongside the main window instance * refactor: update useProfiles and ProfilePage to support profile overrides and improve patchProfilesConfig return type * refactor: enhance handle_success to check main window existence before notifying profile changes * refactor: simplify get_main_window_with_state by using pattern matching and improve window state handling * refactor: fix window activation by removing unnecessary reference in activate_existing_main_window * refactor: remove redundant macOS conditional for window_manager import
This commit is contained in:
parent
6c6e0812b8
commit
c30eaa3678
@ -1,5 +1,6 @@
|
||||
use super::CmdResult;
|
||||
use super::StringifyErr as _;
|
||||
use crate::utils::window_manager::WindowManager;
|
||||
use crate::{
|
||||
config::{
|
||||
Config, IProfiles, PrfItem, PrfOption,
|
||||
@ -310,7 +311,9 @@ async fn handle_success(current_value: Option<&String>) -> CmdResult<bool> {
|
||||
logging!(warn, Type::Cmd, "Warning: 异步保存配置文件失败: {e}");
|
||||
}
|
||||
|
||||
if let Some(current) = current_value {
|
||||
if let Some(current) = current_value
|
||||
&& WindowManager::get_main_window().is_some()
|
||||
{
|
||||
logging!(info, Type::Cmd, "向前端发送配置变更事件: {}", current);
|
||||
handle::Handle::notify_profile_changed(current.to_owned());
|
||||
}
|
||||
|
||||
@ -5,7 +5,7 @@ use std::sync::{
|
||||
Arc,
|
||||
atomic::{AtomicBool, Ordering},
|
||||
};
|
||||
use tauri::{AppHandle, Manager as _, WebviewWindow};
|
||||
use tauri::AppHandle;
|
||||
use tauri_plugin_mihomo::{Mihomo, MihomoExt as _};
|
||||
use tokio::sync::RwLockReadGuard;
|
||||
|
||||
@ -55,10 +55,6 @@ impl Handle {
|
||||
Self::app_handle().mihomo().read().await
|
||||
}
|
||||
|
||||
pub fn get_window() -> Option<WebviewWindow> {
|
||||
Self::app_handle().get_webview_window("main")
|
||||
}
|
||||
|
||||
pub fn refresh_clash() {
|
||||
let handle = Self::global();
|
||||
if handle.is_exiting() {
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
use crate::process::AsyncHandler;
|
||||
use crate::singleton;
|
||||
use crate::utils::notification::{NotificationEvent, notify_event};
|
||||
use crate::utils::window_manager::WindowManager;
|
||||
use crate::{config::Config, core::handle, feat, module::lightweight::entry_lightweight_mode};
|
||||
use anyhow::{Result, bail};
|
||||
use arc_swap::ArcSwap;
|
||||
@ -243,7 +244,7 @@ impl Hotkey {
|
||||
logging!(debug, Type::Hotkey, "Hotkey pressed: {:?}", hotkey_event);
|
||||
let hotkey = hotkey_event.key;
|
||||
if hotkey == Code::KeyQ && is_quit {
|
||||
if let Some(window) = handle::Handle::get_window()
|
||||
if let Some(window) = WindowManager::get_main_window()
|
||||
&& window.is_focused().unwrap_or(false)
|
||||
{
|
||||
logging!(debug, Type::Hotkey, "Executing quit function");
|
||||
@ -260,8 +261,9 @@ impl Hotkey {
|
||||
Self::execute_function(function);
|
||||
} else {
|
||||
use crate::utils::window_manager::WindowManager;
|
||||
let is_visible = WindowManager::is_main_window_visible();
|
||||
let is_focused = WindowManager::is_main_window_focused();
|
||||
let window = WindowManager::get_main_window();
|
||||
let is_visible = WindowManager::is_main_window_visible(window.as_ref());
|
||||
let is_focused = WindowManager::is_main_window_focused(window.as_ref());
|
||||
|
||||
if is_focused && is_visible {
|
||||
Self::execute_function(function);
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
use super::handle::Handle;
|
||||
use crate::constants::timing;
|
||||
use crate::{constants::timing, utils::window_manager::WindowManager};
|
||||
use clash_verge_logging::{Type, logging};
|
||||
use smartstring::alias::String;
|
||||
use std::{sync::mpsc, thread};
|
||||
@ -84,7 +84,7 @@ impl NotificationSystem {
|
||||
None => return,
|
||||
};
|
||||
|
||||
if let Some(window) = super::handle::Handle::get_window() {
|
||||
if let Some(window) = WindowManager::get_main_window() {
|
||||
system.emit_to_window(&window, event);
|
||||
drop(binding);
|
||||
thread::sleep(timing::EVENT_EMIT_DELAY);
|
||||
|
||||
@ -178,7 +178,7 @@ pub async fn hide() {
|
||||
add_light_weight_timer().await;
|
||||
}
|
||||
|
||||
if let Some(window) = handle::Handle::get_window()
|
||||
if let Some(window) = WindowManager::get_main_window()
|
||||
&& window.is_visible().unwrap_or(false)
|
||||
{
|
||||
let _ = window.hide();
|
||||
|
||||
@ -264,7 +264,6 @@ pub fn run() {
|
||||
mod event_handlers {
|
||||
#[cfg(target_os = "macos")]
|
||||
use crate::module::lightweight;
|
||||
#[cfg(target_os = "macos")]
|
||||
use crate::utils::window_manager::WindowManager;
|
||||
use crate::{
|
||||
config::Config,
|
||||
@ -316,7 +315,7 @@ pub fn run() {
|
||||
|
||||
if let tauri::WindowEvent::CloseRequested { api, .. } = api {
|
||||
api.prevent_close();
|
||||
if let Some(window) = core::handle::Handle::get_window() {
|
||||
if let Some(window) = WindowManager::get_main_window() {
|
||||
let _ = window.hide();
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
use crate::{
|
||||
config::Config,
|
||||
core::{handle, timer::Timer, tray::Tray},
|
||||
core::{timer::Timer, tray::Tray},
|
||||
process::AsyncHandler,
|
||||
};
|
||||
|
||||
@ -143,7 +143,7 @@ pub async fn add_light_weight_timer() {
|
||||
}
|
||||
|
||||
fn setup_window_close_listener() {
|
||||
if let Some(window) = handle::Handle::get_window() {
|
||||
if let Some(window) = WindowManager::get_main_window() {
|
||||
let handler_id = window.listen("tauri://close-requested", move |_event| {
|
||||
std::mem::drop(AsyncHandler::spawn(|| async {
|
||||
if let Err(e) = setup_light_weight_timer().await {
|
||||
@ -161,7 +161,7 @@ fn setup_window_close_listener() {
|
||||
}
|
||||
|
||||
fn cancel_window_close_listener() {
|
||||
if let Some(window) = handle::Handle::get_window() {
|
||||
if let Some(window) = WindowManager::get_main_window() {
|
||||
let id = WINDOW_CLOSE_HANDLER_ID.swap(0, Ordering::AcqRel);
|
||||
if id != 0 {
|
||||
window.unlisten(id);
|
||||
@ -171,7 +171,7 @@ fn cancel_window_close_listener() {
|
||||
}
|
||||
|
||||
fn setup_webview_focus_listener() {
|
||||
if let Some(window) = handle::Handle::get_window() {
|
||||
if let Some(window) = WindowManager::get_main_window() {
|
||||
let handler_id = window.listen("tauri://focus", move |_event| {
|
||||
logging_error!(Type::Lightweight, cancel_light_weight_timer());
|
||||
logging!(debug, Type::Lightweight, "监听到窗口获得焦点,取消轻量模式计时");
|
||||
@ -181,7 +181,7 @@ fn setup_webview_focus_listener() {
|
||||
}
|
||||
|
||||
fn cancel_webview_focus_listener() {
|
||||
if let Some(window) = handle::Handle::get_window() {
|
||||
if let Some(window) = WindowManager::get_main_window() {
|
||||
let id = WEBVIEW_FOCUS_HANDLER_ID.swap(0, Ordering::AcqRel);
|
||||
if id != 0 {
|
||||
window.unlisten(id);
|
||||
|
||||
@ -59,6 +59,28 @@ fn should_handle_window_operation() -> bool {
|
||||
pub struct WindowManager;
|
||||
|
||||
impl WindowManager {
|
||||
pub fn get_main_window_with_state() -> (Option<WebviewWindow<Wry>>, WindowState) {
|
||||
let Some(window) = Self::get_main_window() else {
|
||||
return (None, WindowState::NotExist);
|
||||
};
|
||||
|
||||
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);
|
||||
|
||||
let state = if is_minimized {
|
||||
WindowState::Minimized
|
||||
} else if !is_visible {
|
||||
WindowState::Hidden
|
||||
} else if is_focused {
|
||||
WindowState::VisibleFocused
|
||||
} else {
|
||||
WindowState::VisibleUnfocused
|
||||
};
|
||||
|
||||
(Some(window), state)
|
||||
}
|
||||
|
||||
pub fn get_main_window_state() -> WindowState {
|
||||
match Self::get_main_window() {
|
||||
Some(window) => {
|
||||
@ -119,12 +141,12 @@ impl WindowManager {
|
||||
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, "窗口在检查期间已变为可见和有焦点状态");
|
||||
return WindowOperationResult::NoAction;
|
||||
}
|
||||
let (window, state_after_check) = Self::get_main_window_with_state();
|
||||
if state_after_check == WindowState::VisibleFocused {
|
||||
logging!(info, Type::Window, "窗口在检查期间已变为可见和有焦点状态");
|
||||
return WindowOperationResult::NoAction;
|
||||
}
|
||||
if let Some(window) = window {
|
||||
Self::activate_window(&window)
|
||||
} else {
|
||||
WindowOperationResult::Failed
|
||||
@ -135,25 +157,18 @@ impl WindowManager {
|
||||
|
||||
/// 切换主窗口显示状态(显示/隐藏)
|
||||
pub async fn toggle_main_window() -> WindowOperationResult {
|
||||
// 防抖检查
|
||||
if !should_handle_window_operation() {
|
||||
return WindowOperationResult::NoAction;
|
||||
};
|
||||
logging!(info, Type::Window, "开始切换主窗口显示状态");
|
||||
}
|
||||
|
||||
let current_state = Self::get_main_window_state();
|
||||
logging!(
|
||||
info,
|
||||
Type::Window,
|
||||
"当前窗口状态: {:?} | 详细状态: {}",
|
||||
current_state,
|
||||
Self::get_window_status_info()
|
||||
);
|
||||
let (window, state) = Self::get_main_window_with_state();
|
||||
|
||||
match current_state {
|
||||
logging!(debug, Type::Window, "当前状态: {:?}", state);
|
||||
|
||||
match state {
|
||||
WindowState::NotExist => Self::handle_not_exist_toggle().await,
|
||||
WindowState::VisibleFocused | WindowState::VisibleUnfocused => Self::hide_main_window(),
|
||||
WindowState::Minimized | WindowState::Hidden => Self::activate_existing_main_window(),
|
||||
WindowState::VisibleFocused | WindowState::VisibleUnfocused => Self::hide_main_window(window.as_ref()),
|
||||
WindowState::Minimized | WindowState::Hidden => Self::activate_existing_main_window(window.as_ref()),
|
||||
}
|
||||
}
|
||||
|
||||
@ -169,9 +184,9 @@ impl WindowManager {
|
||||
}
|
||||
|
||||
// 隐藏主窗口
|
||||
fn hide_main_window() -> WindowOperationResult {
|
||||
fn hide_main_window(window: Option<&WebviewWindow<Wry>>) -> WindowOperationResult {
|
||||
logging!(info, Type::Window, "窗口可见,将隐藏窗口");
|
||||
if let Some(window) = Self::get_main_window() {
|
||||
if let Some(window) = window {
|
||||
match window.hide() {
|
||||
Ok(_) => {
|
||||
logging!(info, Type::Window, "窗口已成功隐藏");
|
||||
@ -189,10 +204,10 @@ impl WindowManager {
|
||||
}
|
||||
|
||||
// 激活已存在的主窗口
|
||||
fn activate_existing_main_window() -> WindowOperationResult {
|
||||
fn activate_existing_main_window(window: Option<&WebviewWindow<Wry>>) -> WindowOperationResult {
|
||||
logging!(info, Type::Window, "窗口存在但被隐藏或最小化,将激活窗口");
|
||||
if let Some(window) = Self::get_main_window() {
|
||||
Self::activate_window(&window)
|
||||
if let Some(window) = window {
|
||||
Self::activate_window(window)
|
||||
} else {
|
||||
logging!(warn, Type::Window, "无法获取窗口实例");
|
||||
WindowOperationResult::Failed
|
||||
@ -255,24 +270,18 @@ impl WindowManager {
|
||||
}
|
||||
|
||||
/// 检查窗口是否可见
|
||||
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_visible(window: Option<&WebviewWindow<Wry>>) -> bool {
|
||||
window.map(|w| w.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_focused(window: Option<&WebviewWindow<Wry>>) -> bool {
|
||||
window.map(|w| w.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)
|
||||
pub fn is_main_window_minimized(window: Option<&WebviewWindow<Wry>>) -> bool {
|
||||
window.map(|w| w.is_minimized().unwrap_or(false)).unwrap_or(false)
|
||||
}
|
||||
|
||||
/// 创建新窗口,防抖避免重复调用
|
||||
@ -320,11 +329,11 @@ impl WindowManager {
|
||||
}
|
||||
|
||||
/// 获取详细的窗口状态信息
|
||||
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();
|
||||
fn get_window_status_info() -> String {
|
||||
let (window, state) = Self::get_main_window_with_state();
|
||||
let is_visible = Self::is_main_window_visible(window.as_ref());
|
||||
let is_focused = Self::is_main_window_focused(window.as_ref());
|
||||
let is_minimized = Self::is_main_window_minimized(window.as_ref());
|
||||
|
||||
format!("窗口状态: {state:?} | 可见: {is_visible} | 有焦点: {is_focused} | 最小化: {is_minimized}")
|
||||
}
|
||||
|
||||
@ -2,11 +2,11 @@ import useSWR, { mutate } from "swr";
|
||||
import { selectNodeForGroup } from "tauri-plugin-mihomo-api";
|
||||
|
||||
import {
|
||||
calcuProxies,
|
||||
getProfiles,
|
||||
patchProfile,
|
||||
patchProfilesConfig,
|
||||
} from "@/services/cmds";
|
||||
import { calcuProxies } from "@/services/cmds";
|
||||
import { debugLog } from "@/utils/debug";
|
||||
|
||||
export const useProfiles = () => {
|
||||
@ -36,6 +36,7 @@ export const useProfiles = () => {
|
||||
const patchProfiles = async (
|
||||
value: Partial<IProfilesConfig>,
|
||||
signal?: AbortSignal,
|
||||
options?: { deferRefreshOnSuccess?: boolean },
|
||||
) => {
|
||||
try {
|
||||
if (signal?.aborted) {
|
||||
@ -47,7 +48,9 @@ export const useProfiles = () => {
|
||||
throw new DOMException("Operation was aborted", "AbortError");
|
||||
}
|
||||
|
||||
await mutateProfiles();
|
||||
if (!options?.deferRefreshOnSuccess || !success) {
|
||||
await mutateProfiles();
|
||||
}
|
||||
|
||||
return success;
|
||||
} catch (error) {
|
||||
@ -70,16 +73,14 @@ export const useProfiles = () => {
|
||||
};
|
||||
|
||||
// 根据selected的节点选择
|
||||
const activateSelected = async () => {
|
||||
const activateSelected = async (profileOverride?: IProfilesConfig) => {
|
||||
try {
|
||||
debugLog("[ActivateSelected] 开始处理代理选择");
|
||||
|
||||
const [proxiesData, profileData] = await Promise.all([
|
||||
calcuProxies(),
|
||||
getProfiles(),
|
||||
]);
|
||||
const proxiesData = await calcuProxies();
|
||||
const profileData = profileOverride ?? profiles;
|
||||
|
||||
if (!profileData || !proxiesData) {
|
||||
if (!profileData || !proxiesData || !profileData.items) {
|
||||
debugLog("[ActivateSelected] 代理或配置数据不可用,跳过处理");
|
||||
return;
|
||||
}
|
||||
|
||||
@ -389,7 +389,7 @@ const ProfilePage = () => {
|
||||
switchingProfileRef.current === profile &&
|
||||
!abortController.signal.aborted
|
||||
) {
|
||||
await activateSelected();
|
||||
await activateSelected(profiles);
|
||||
debugLog(`[Profile] 后台处理完成,序列号: ${sequence}`);
|
||||
} else {
|
||||
debugProfileSwitch(
|
||||
@ -402,7 +402,7 @@ const ProfilePage = () => {
|
||||
console.warn("Failed to activate selected proxies:", err);
|
||||
}
|
||||
},
|
||||
[activateSelected],
|
||||
[activateSelected, profiles],
|
||||
);
|
||||
|
||||
const activateProfile = useCallback(
|
||||
@ -456,6 +456,7 @@ const ProfilePage = () => {
|
||||
const requestPromise = patchProfiles(
|
||||
{ current: profile },
|
||||
currentAbortController.signal,
|
||||
{ deferRefreshOnSuccess: true },
|
||||
);
|
||||
pendingRequestRef.current = requestPromise;
|
||||
|
||||
|
||||
@ -18,7 +18,7 @@ export async function enhanceProfiles() {
|
||||
}
|
||||
|
||||
export async function patchProfilesConfig(profiles: IProfilesConfig) {
|
||||
return invoke<void>("patch_profiles_config", { profiles });
|
||||
return invoke<boolean>("patch_profiles_config", { profiles });
|
||||
}
|
||||
|
||||
export async function createProfile(
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user