mirror of
https://github.com/clash-verge-rev/clash-verge-rev.git
synced 2026-04-13 05:20:28 +08:00
fix: resolve issue with tray operations after system resume (#6216)
* feat(limiter): add Limiter struct with clock interface and tests * feat(limiter): integrate Limiter into tray and window management for rate limiting * fix(tray, window_manager): update debounce timing for tray click and window operations * refactor(limiter): change time representation from u64 to u128 for improved precision * fix: resolve issue with tray operations after system resume * Revert "refactor(limiter): change time representation from u64 to u128 for improved precision" This reverts commit 2198f40f7fcecbb755deb38af005c28e993db970.
This commit is contained in:
parent
654152391b
commit
ae5d3c478a
74
Cargo.lock
generated
74
Cargo.lock
generated
@ -1122,6 +1122,7 @@ dependencies = [
|
|||||||
"chrono",
|
"chrono",
|
||||||
"clash-verge-draft",
|
"clash-verge-draft",
|
||||||
"clash-verge-i18n",
|
"clash-verge-i18n",
|
||||||
|
"clash-verge-limiter",
|
||||||
"clash-verge-logging",
|
"clash-verge-logging",
|
||||||
"clash-verge-signal",
|
"clash-verge-signal",
|
||||||
"clash_verge_logger",
|
"clash_verge_logger",
|
||||||
@ -1137,7 +1138,6 @@ dependencies = [
|
|||||||
"futures",
|
"futures",
|
||||||
"gethostname",
|
"gethostname",
|
||||||
"getrandom 0.3.4",
|
"getrandom 0.3.4",
|
||||||
"governor",
|
|
||||||
"log",
|
"log",
|
||||||
"nanoid",
|
"nanoid",
|
||||||
"network-interface",
|
"network-interface",
|
||||||
@ -1199,6 +1199,10 @@ dependencies = [
|
|||||||
"sys-locale",
|
"sys-locale",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "clash-verge-limiter"
|
||||||
|
version = "0.1.0"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clash-verge-logging"
|
name = "clash-verge-logging"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
@ -2640,12 +2644,6 @@ version = "0.3.31"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988"
|
checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "futures-timer"
|
|
||||||
version = "3.0.3"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "f288b0a4f20f9a56b5d1da57e2227c661b7b16168e2f72365f57b63326e29b24"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "futures-util"
|
name = "futures-util"
|
||||||
version = "0.3.31"
|
version = "0.3.31"
|
||||||
@ -2978,29 +2976,6 @@ dependencies = [
|
|||||||
"system-deps",
|
"system-deps",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "governor"
|
|
||||||
version = "0.10.4"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "9efcab3c1958580ff1f25a2a41be1668f7603d849bb63af523b208a3cc1223b8"
|
|
||||||
dependencies = [
|
|
||||||
"cfg-if",
|
|
||||||
"dashmap 6.1.0",
|
|
||||||
"futures-sink",
|
|
||||||
"futures-timer",
|
|
||||||
"futures-util",
|
|
||||||
"getrandom 0.3.4",
|
|
||||||
"hashbrown 0.16.1",
|
|
||||||
"nonzero_ext",
|
|
||||||
"parking_lot",
|
|
||||||
"portable-atomic",
|
|
||||||
"quanta",
|
|
||||||
"rand 0.9.2",
|
|
||||||
"smallvec",
|
|
||||||
"spinning_top",
|
|
||||||
"web-time",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "gtk"
|
name = "gtk"
|
||||||
version = "0.18.2"
|
version = "0.18.2"
|
||||||
@ -4408,12 +4383,6 @@ dependencies = [
|
|||||||
"memchr",
|
"memchr",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "nonzero_ext"
|
|
||||||
version = "0.3.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "38bf9645c8b145698bb0b18a4637dcacbc421ea49bef2317e4fd8065a387cf21"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "normpath"
|
name = "normpath"
|
||||||
version = "1.5.0"
|
version = "1.5.0"
|
||||||
@ -5580,21 +5549,6 @@ dependencies = [
|
|||||||
"num-traits",
|
"num-traits",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "quanta"
|
|
||||||
version = "0.12.6"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "f3ab5a9d756f0d97bdc89019bd2e4ea098cf9cde50ee7564dde6b81ccc8f06c7"
|
|
||||||
dependencies = [
|
|
||||||
"crossbeam-utils",
|
|
||||||
"libc",
|
|
||||||
"once_cell",
|
|
||||||
"raw-cpuid",
|
|
||||||
"wasi 0.11.1+wasi-snapshot-preview1",
|
|
||||||
"web-sys",
|
|
||||||
"winapi",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "quick-error"
|
name = "quick-error"
|
||||||
version = "2.0.1"
|
version = "2.0.1"
|
||||||
@ -5809,15 +5763,6 @@ dependencies = [
|
|||||||
"rand_core 0.5.1",
|
"rand_core 0.5.1",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "raw-cpuid"
|
|
||||||
version = "11.6.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "498cd0dc59d73224351ee52a95fee0f1a617a2eae0e7d9d720cc622c73a54186"
|
|
||||||
dependencies = [
|
|
||||||
"bitflags 2.10.0",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "raw-window-handle"
|
name = "raw-window-handle"
|
||||||
version = "0.6.2"
|
version = "0.6.2"
|
||||||
@ -7020,15 +6965,6 @@ dependencies = [
|
|||||||
"system-deps",
|
"system-deps",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "spinning_top"
|
|
||||||
version = "0.3.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "d96d2d1d716fb500937168cc09353ffdc7a012be8475ac7308e1bdf0e3923300"
|
|
||||||
dependencies = [
|
|
||||||
"lock_api",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "stable_deref_trait"
|
name = "stable_deref_trait"
|
||||||
version = "1.2.1"
|
version = "1.2.1"
|
||||||
|
|||||||
@ -6,6 +6,7 @@ members = [
|
|||||||
"crates/clash-verge-signal",
|
"crates/clash-verge-signal",
|
||||||
"crates/tauri-plugin-clash-verge-sysinfo",
|
"crates/tauri-plugin-clash-verge-sysinfo",
|
||||||
"crates/clash-verge-i18n",
|
"crates/clash-verge-i18n",
|
||||||
|
"crates/clash-verge-limiter",
|
||||||
]
|
]
|
||||||
resolver = "2"
|
resolver = "2"
|
||||||
|
|
||||||
@ -44,6 +45,7 @@ clash-verge-draft = { path = "crates/clash-verge-draft" }
|
|||||||
clash-verge-logging = { path = "crates/clash-verge-logging" }
|
clash-verge-logging = { path = "crates/clash-verge-logging" }
|
||||||
clash-verge-signal = { path = "crates/clash-verge-signal" }
|
clash-verge-signal = { path = "crates/clash-verge-signal" }
|
||||||
clash-verge-i18n = { path = "crates/clash-verge-i18n" }
|
clash-verge-i18n = { path = "crates/clash-verge-i18n" }
|
||||||
|
clash-verge-limiter = { path = "crates/clash-verge-limiter" }
|
||||||
tauri-plugin-clash-verge-sysinfo = { path = "crates/tauri-plugin-clash-verge-sysinfo" }
|
tauri-plugin-clash-verge-sysinfo = { path = "crates/tauri-plugin-clash-verge-sysinfo" }
|
||||||
|
|
||||||
tauri = { version = "2.9.5" }
|
tauri = { version = "2.9.5" }
|
||||||
|
|||||||
@ -7,6 +7,7 @@
|
|||||||
- 修复 WebDAV 页面重试逻辑
|
- 修复 WebDAV 页面重试逻辑
|
||||||
- 修复 Linux 通过 GUI 安装服务模式权限不符合预期
|
- 修复 Linux 通过 GUI 安装服务模式权限不符合预期
|
||||||
- 修复 macOS 因网口顺序导致无法正确设置代理
|
- 修复 macOS 因网口顺序导致无法正确设置代理
|
||||||
|
- 修复恢复休眠后无法操作托盘
|
||||||
|
|
||||||
<details>
|
<details>
|
||||||
<summary><strong> ✨ 新增功能 </strong></summary>
|
<summary><strong> ✨ 新增功能 </strong></summary>
|
||||||
|
|||||||
9
crates/clash-verge-limiter/Cargo.toml
Normal file
9
crates/clash-verge-limiter/Cargo.toml
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
[package]
|
||||||
|
name = "clash-verge-limiter"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2024"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
|
||||||
|
[lints]
|
||||||
|
workspace = true
|
||||||
165
crates/clash-verge-limiter/src/lib.rs
Normal file
165
crates/clash-verge-limiter/src/lib.rs
Normal file
@ -0,0 +1,165 @@
|
|||||||
|
use std::sync::Arc;
|
||||||
|
use std::sync::atomic::{AtomicU64, Ordering};
|
||||||
|
use std::time::{Duration, SystemTime, UNIX_EPOCH};
|
||||||
|
|
||||||
|
pub type SystemLimiter = Limiter<SystemClock>;
|
||||||
|
|
||||||
|
pub trait Clock: Send + Sync {
|
||||||
|
fn now_ms(&self) -> u64;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Clock + ?Sized> Clock for &T {
|
||||||
|
fn now_ms(&self) -> u64 {
|
||||||
|
(**self).now_ms()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Clock + ?Sized> Clock for Arc<T> {
|
||||||
|
fn now_ms(&self) -> u64 {
|
||||||
|
(**self).now_ms()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct SystemClock;
|
||||||
|
|
||||||
|
impl Clock for SystemClock {
|
||||||
|
fn now_ms(&self) -> u64 {
|
||||||
|
SystemTime::now()
|
||||||
|
.duration_since(UNIX_EPOCH)
|
||||||
|
.unwrap_or_default()
|
||||||
|
.as_millis() as u64
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Limiter<C: Clock = SystemClock> {
|
||||||
|
last_run_ms: AtomicU64,
|
||||||
|
period_ms: u64,
|
||||||
|
clock: C,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<C: Clock> Limiter<C> {
|
||||||
|
pub const fn new(period: Duration, clock: C) -> Self {
|
||||||
|
Self {
|
||||||
|
last_run_ms: AtomicU64::new(0),
|
||||||
|
period_ms: period.as_millis() as u64,
|
||||||
|
clock,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn check(&self) -> bool {
|
||||||
|
let now = self.clock.now_ms();
|
||||||
|
let last = self.last_run_ms.load(Ordering::Relaxed);
|
||||||
|
|
||||||
|
if now < last + self.period_ms && now >= last {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.last_run_ms
|
||||||
|
.compare_exchange(last, now, Ordering::SeqCst, Ordering::Relaxed)
|
||||||
|
.is_ok()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod extra_tests {
|
||||||
|
use super::*;
|
||||||
|
use std::sync::Arc;
|
||||||
|
use std::thread;
|
||||||
|
|
||||||
|
struct MockClock(AtomicU64);
|
||||||
|
impl Clock for MockClock {
|
||||||
|
fn now_ms(&self) -> u64 {
|
||||||
|
self.0.load(Ordering::SeqCst)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_zero_period_always_passes() {
|
||||||
|
let mock = MockClock(AtomicU64::new(100));
|
||||||
|
let limiter = Limiter::new(Duration::from_millis(0), &mock);
|
||||||
|
|
||||||
|
assert!(limiter.check());
|
||||||
|
assert!(limiter.check());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_boundary_condition() {
|
||||||
|
let period_ms = 100;
|
||||||
|
let mock = MockClock(AtomicU64::new(1000));
|
||||||
|
let limiter = Limiter::new(Duration::from_millis(period_ms), &mock);
|
||||||
|
|
||||||
|
assert!(limiter.check());
|
||||||
|
|
||||||
|
mock.0.store(1099, Ordering::SeqCst);
|
||||||
|
assert!(!limiter.check());
|
||||||
|
|
||||||
|
mock.0.store(1100, Ordering::SeqCst);
|
||||||
|
assert!(limiter.check(), "Should pass exactly at period boundary");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_high_concurrency_consistency() {
|
||||||
|
let period = Duration::from_millis(1000);
|
||||||
|
let mock = Arc::new(MockClock(AtomicU64::new(1000)));
|
||||||
|
let limiter = Arc::new(Limiter::new(period, Arc::clone(&mock)));
|
||||||
|
|
||||||
|
assert!(limiter.check());
|
||||||
|
|
||||||
|
mock.0.store(2500, Ordering::SeqCst);
|
||||||
|
|
||||||
|
let mut handles = vec![];
|
||||||
|
for _ in 0..20 {
|
||||||
|
let l = Arc::clone(&limiter);
|
||||||
|
handles.push(thread::spawn(move || l.check()));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::unwrap_used)]
|
||||||
|
let results: Vec<bool> = handles.into_iter().map(|h| h.join().unwrap()).collect();
|
||||||
|
|
||||||
|
let success_count = results.iter().filter(|&&x| x).count();
|
||||||
|
assert_eq!(success_count, 1);
|
||||||
|
|
||||||
|
assert_eq!(limiter.last_run_ms.load(Ordering::SeqCst), 2500);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_extreme_time_jump() {
|
||||||
|
let mock = MockClock(AtomicU64::new(100));
|
||||||
|
let limiter = Limiter::new(Duration::from_millis(100), &mock);
|
||||||
|
|
||||||
|
assert!(limiter.check());
|
||||||
|
|
||||||
|
mock.0.store(u64::MAX - 10, Ordering::SeqCst);
|
||||||
|
assert!(limiter.check());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_system_clock_real_path() {
|
||||||
|
let clock = SystemClock;
|
||||||
|
let start = clock.now_ms();
|
||||||
|
assert!(start > 0);
|
||||||
|
|
||||||
|
std::thread::sleep(Duration::from_millis(10));
|
||||||
|
assert!(clock.now_ms() >= start);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_limiter_with_system_clock_default() {
|
||||||
|
let limiter = Limiter::new(Duration::from_millis(100), SystemClock);
|
||||||
|
assert!(limiter.check());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_coverage_time_backward() {
|
||||||
|
let mock = MockClock(AtomicU64::new(5000));
|
||||||
|
let limiter = Limiter::new(Duration::from_millis(100), &mock);
|
||||||
|
|
||||||
|
assert!(limiter.check());
|
||||||
|
|
||||||
|
mock.0.store(4000, Ordering::SeqCst);
|
||||||
|
|
||||||
|
assert!(limiter.check(), "Should pass and reset when time moves backward");
|
||||||
|
|
||||||
|
assert_eq!(limiter.last_run_ms.load(Ordering::SeqCst), 4000);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -35,6 +35,7 @@ clash-verge-draft = { workspace = true }
|
|||||||
clash-verge-logging = { workspace = true }
|
clash-verge-logging = { workspace = true }
|
||||||
clash-verge-signal = { workspace = true }
|
clash-verge-signal = { workspace = true }
|
||||||
clash-verge-i18n = { workspace = true }
|
clash-verge-i18n = { workspace = true }
|
||||||
|
clash-verge-limiter = { workspace = true }
|
||||||
tauri-plugin-clash-verge-sysinfo = { workspace = true }
|
tauri-plugin-clash-verge-sysinfo = { workspace = true }
|
||||||
tauri-plugin-clipboard-manager = { workspace = true }
|
tauri-plugin-clipboard-manager = { workspace = true }
|
||||||
tauri = { workspace = true, features = [
|
tauri = { workspace = true, features = [
|
||||||
@ -105,7 +106,6 @@ arc-swap = "1.8.0"
|
|||||||
rust_iso3166 = "0.1.14"
|
rust_iso3166 = "0.1.14"
|
||||||
# Use the git repo until the next release after v2.0.0.
|
# Use the git repo until the next release after v2.0.0.
|
||||||
dark-light = { git = "https://github.com/rust-dark-light/dark-light" }
|
dark-light = { git = "https://github.com/rust-dark-light/dark-light" }
|
||||||
governor = "0.10.4"
|
|
||||||
|
|
||||||
[target.'cfg(windows)'.dependencies]
|
[target.'cfg(windows)'.dependencies]
|
||||||
deelevate = { workspace = true }
|
deelevate = { workspace = true }
|
||||||
|
|||||||
@ -9,7 +9,7 @@ use crate::{
|
|||||||
Type, cmd, config::Config, feat, logging, module::lightweight::is_in_lightweight_mode,
|
Type, cmd, config::Config, feat, logging, module::lightweight::is_in_lightweight_mode,
|
||||||
utils::dirs::find_target_icons,
|
utils::dirs::find_target_icons,
|
||||||
};
|
};
|
||||||
use governor::{DefaultDirectRateLimiter, Quota, RateLimiter};
|
use clash_verge_limiter::{Limiter, SystemClock, SystemLimiter};
|
||||||
use tauri::tray::TrayIconBuilder;
|
use tauri::tray::TrayIconBuilder;
|
||||||
use tauri_plugin_clash_verge_sysinfo::is_current_app_handle_admin;
|
use tauri_plugin_clash_verge_sysinfo::is_current_app_handle_admin;
|
||||||
use tauri_plugin_mihomo::models::Proxies;
|
use tauri_plugin_mihomo::models::Proxies;
|
||||||
@ -19,7 +19,6 @@ use super::handle;
|
|||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use smartstring::alias::String;
|
use smartstring::alias::String;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::num::NonZeroU32;
|
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use tauri::{
|
use tauri::{
|
||||||
AppHandle, Wry,
|
AppHandle, Wry,
|
||||||
@ -33,13 +32,13 @@ use menu_def::{MenuIds, MenuTexts};
|
|||||||
|
|
||||||
type ProxyMenuItem = (Option<Submenu<Wry>>, Vec<Box<dyn IsMenuItem<Wry>>>);
|
type ProxyMenuItem = (Option<Submenu<Wry>>, Vec<Box<dyn IsMenuItem<Wry>>>);
|
||||||
|
|
||||||
const TRAY_CLICK_DEBOUNCE_MS: u64 = 1_275;
|
const TRAY_CLICK_DEBOUNCE_MS: u64 = 300;
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
struct TrayState {}
|
struct TrayState {}
|
||||||
|
|
||||||
pub struct Tray {
|
pub struct Tray {
|
||||||
limiter: DefaultDirectRateLimiter,
|
limiter: SystemLimiter,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TrayState {
|
impl TrayState {
|
||||||
@ -136,11 +135,7 @@ impl Default for Tray {
|
|||||||
#[allow(clippy::unwrap_used)]
|
#[allow(clippy::unwrap_used)]
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
limiter: RateLimiter::direct(
|
limiter: Limiter::new(Duration::from_millis(TRAY_CLICK_DEBOUNCE_MS), SystemClock),
|
||||||
Quota::with_period(Duration::from_millis(TRAY_CLICK_DEBOUNCE_MS))
|
|
||||||
.unwrap()
|
|
||||||
.allow_burst(NonZeroU32::new(1).unwrap()),
|
|
||||||
),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -462,11 +457,11 @@ impl Tray {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn should_handle_tray_click(&self) -> bool {
|
fn should_handle_tray_click(&self) -> bool {
|
||||||
let res = self.limiter.check().is_ok();
|
let allow = self.limiter.check();
|
||||||
if !res {
|
if !allow {
|
||||||
logging!(debug, Type::Tray, "tray click rate limited");
|
logging!(debug, Type::Tray, "tray click rate limited");
|
||||||
}
|
}
|
||||||
res
|
allow
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,8 +1,7 @@
|
|||||||
use crate::{core::handle, utils::resolve::window::build_new_window};
|
use crate::{core::handle, utils::resolve::window::build_new_window};
|
||||||
|
use clash_verge_limiter::Limiter;
|
||||||
use clash_verge_logging::{Type, logging};
|
use clash_verge_logging::{Type, logging};
|
||||||
use governor::{DefaultDirectRateLimiter, Quota, RateLimiter};
|
|
||||||
use once_cell::sync::Lazy;
|
use once_cell::sync::Lazy;
|
||||||
use std::num::NonZeroU32;
|
|
||||||
use std::pin::Pin;
|
use std::pin::Pin;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use tauri::{Manager as _, WebviewWindow, Wry};
|
use tauri::{Manager as _, WebviewWindow, Wry};
|
||||||
@ -40,22 +39,20 @@ pub enum WindowState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 窗口操作防抖机制
|
// 窗口操作防抖机制
|
||||||
const WINDOW_OPERATION_DEBOUNCE_MS: u64 = 1_275;
|
const WINDOW_OPERATION_DEBOUNCE_MS: u64 = 625;
|
||||||
static WINDOW_OPERATION_LIMITER: Lazy<DefaultDirectRateLimiter> = Lazy::new(|| {
|
static WINDOW_OPERATION_LIMITER: Lazy<Limiter> = Lazy::new(|| {
|
||||||
#[allow(clippy::unwrap_used)]
|
Limiter::new(
|
||||||
RateLimiter::direct(
|
Duration::from_millis(WINDOW_OPERATION_DEBOUNCE_MS),
|
||||||
Quota::with_period(Duration::from_millis(WINDOW_OPERATION_DEBOUNCE_MS))
|
clash_verge_limiter::SystemClock,
|
||||||
.unwrap()
|
|
||||||
.allow_burst(NonZeroU32::new(1).unwrap()),
|
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
|
|
||||||
fn should_handle_window_operation() -> bool {
|
fn should_handle_window_operation() -> bool {
|
||||||
let res = WINDOW_OPERATION_LIMITER.check().is_ok();
|
let allow = WINDOW_OPERATION_LIMITER.check();
|
||||||
if !res {
|
if !allow {
|
||||||
logging!(debug, Type::Window, "window operation rate limited");
|
logging!(debug, Type::Window, "window operation rate limited");
|
||||||
}
|
}
|
||||||
res
|
allow
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 统一的窗口管理器
|
/// 统一的窗口管理器
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user