feat(signal): add clash-verge-signal crate and integrate signal handling (#5500)

* feat(signal): add clash-verge-signal crate and integrate signal handling

* fix: clippy error on type complexity
This commit is contained in:
Tunglies
2025-11-18 10:04:21 +08:00
committed by GitHub
parent 16478d314b
commit e672d19622
11 changed files with 149 additions and 79 deletions

14
Cargo.lock generated
View File

@@ -1117,6 +1117,7 @@ dependencies = [
"chrono",
"clash-verge-draft",
"clash-verge-logging",
"clash-verge-signal",
"clash_verge_logger",
"clash_verge_service_ipc",
"compact_str",
@@ -1147,7 +1148,6 @@ dependencies = [
"serde",
"serde_json",
"serde_yaml_ng",
"signal-hook 0.3.18",
"smartstring",
"sys-locale",
"sysinfo",
@@ -1172,7 +1172,6 @@ dependencies = [
"tokio-stream",
"warp",
"winapi",
"windows-sys 0.61.2",
"winreg 0.55.0",
"zip 6.0.0",
]
@@ -1197,6 +1196,17 @@ dependencies = [
"tokio",
]
[[package]]
name = "clash-verge-signal"
version = "0.1.0"
dependencies = [
"clash-verge-logging",
"log",
"signal-hook 0.3.18",
"tauri",
"windows-sys 0.61.2",
]
[[package]]
name = "clash_verge_logger"
version = "0.2.1"

View File

@@ -3,6 +3,7 @@ members = [
"src-tauri",
"crates/clash-verge-draft",
"crates/clash-verge-logging",
"crates/clash-verge-signal",
]
resolver = "2"
@@ -42,6 +43,8 @@ strip = false
[workspace.dependencies]
clash-verge-draft = { path = "crates/clash-verge-draft" }
clash-verge-logging = { path = "crates/clash-verge-logging" }
clash-verge-signal = { path = "crates/clash-verge-signal" }
tauri = { version = "2.9.3" }
parking_lot = { version = "0.12.5", features = [
"hardware-lock-elision",
"send_guard",

View File

@@ -0,0 +1,25 @@
[package]
name = "clash-verge-signal"
version = "0.1.0"
edition.workspace = true
rust-version.workspace = true
[dependencies]
tauri = { workspace = true }
clash-verge-logging = { workspace = true }
log = { workspace = true }
[target.'cfg(unix)'.dependencies]
signal-hook = "0.3.18"
[target.'cfg(windows)'.dependencies]
windows-sys = { version = "0.61.2", features = [
"Win32_Foundation",
"Win32_Graphics_Gdi",
"Win32_System_SystemServices",
"Win32_UI_WindowsAndMessaging",
] }
[lints]
workspace = true

View File

@@ -0,0 +1,16 @@
#[cfg(unix)]
mod unix;
#[cfg(windows)]
mod windows;
pub fn register<F, Fut>(#[cfg(windows)] app_handle: &tauri::AppHandle, f: F)
where
F: Fn() -> Fut + Send + Sync + 'static,
Fut: Future + Send + 'static,
{
#[cfg(unix)]
unix::register(f);
#[cfg(windows)]
windows::register(app_handle, f);
}

View File

@@ -0,0 +1,44 @@
use signal_hook::{
consts::{SIGHUP, SIGINT, SIGTERM},
iterator::Signals,
low_level,
};
use clash_verge_logging::{Type, logging, logging_error};
pub fn register<F, Fut>(f: F)
where
F: Fn() -> Fut + Send + Sync + 'static,
Fut: Future + Send + 'static,
{
tauri::async_runtime::spawn(async move {
let signals = [SIGTERM, SIGINT, SIGHUP];
let mut sigs = match Signals::new(signals) {
Ok(s) => s,
Err(e) => {
logging!(error, Type::System, "注册信号处理器失败: {}", e);
return;
}
};
for signal in &mut sigs {
let signal_to_str = |signal: i32| match signal {
SIGTERM => "SIGTERM",
SIGINT => "SIGINT",
SIGHUP => "SIGHUP",
_ => "UNKNOWN",
};
logging!(info, Type::System, "捕获到信号 {}", signal_to_str(signal));
f().await;
logging_error!(
Type::System,
"信号 {:?} 默认处理失败",
low_level::emulate_default_handler(signal)
);
}
});
}

View File

@@ -1,4 +1,6 @@
use tauri::Manager as _;
use std::{future::Future, pin::Pin, sync::OnceLock};
use tauri::{AppHandle, Manager as _};
use windows_sys::Win32::{
Foundation::{HWND, LPARAM, LRESULT, WPARAM},
UI::WindowsAndMessaging::{
@@ -8,13 +10,17 @@ use windows_sys::Win32::{
},
};
use crate::{core::handle, feat};
use clash_verge_logging::{Type, logging};
// code refer to:
// global-hotkey (https://github.com/tauri-apps/global-hotkey)
// Global Shortcut (https://github.com/tauri-apps/plugins-workspace/tree/v2/plugins/global-shortcut)
type ShutdownHandler =
Box<dyn Fn() -> Pin<Box<dyn std::future::Future<Output = ()> + Send>> + Send + Sync>;
static SHUTDOWN_HANDLER: OnceLock<ShutdownHandler> = OnceLock::new();
struct ShutdownState {
hwnd: HWND,
}
@@ -49,11 +55,19 @@ unsafe extern "system" fn shutdown_proc(
);
}
WM_ENDSESSION => {
tauri::async_runtime::block_on(async move {
logging!(info, Type::System, "Session ended, system shutting down.");
feat::clean_async().await;
logging!(info, Type::System, "resolved reset finished");
});
if let Some(handler) = SHUTDOWN_HANDLER.get() {
tauri::async_runtime::block_on(async {
logging!(info, Type::System, "Session ended, system shutting down.");
handler().await;
logging!(info, Type::System, "resolved reset finished");
});
} else {
logging!(
error,
Type::System,
"WM_ENDSESSION received but no shutdown handler is registered"
);
}
}
_ => {}
};
@@ -81,8 +95,18 @@ fn get_instance_handle() -> windows_sys::Win32::Foundation::HMODULE {
unsafe { &__ImageBase as *const _ as _ }
}
pub fn register() {
let app_handle = handle::Handle::app_handle();
pub fn register<F, Fut>(app_handle: &AppHandle, f: F)
where
F: Fn() -> Fut + Send + Sync + 'static,
Fut: Future + Send + 'static,
{
let _ = SHUTDOWN_HANDLER.set(Box::new(move || {
let fut = (f)();
Box::pin(async move {
fut.await;
}) as Pin<Box<dyn std::future::Future<Output = ()> + Send>>
}));
let class_name = encode_wide("global_shutdown_app");
unsafe {
let hinstance = get_instance_handle();

View File

@@ -32,6 +32,14 @@ tauri-build = { version = "2.5.2", features = [] }
[dependencies]
clash-verge-draft = { workspace = true }
clash-verge-logging = { workspace = true }
clash-verge-signal = { workspace = true }
tauri = { workspace = true, features = [
"protocol-asset",
"devtools",
"tray-icon",
"image-ico",
"image-png",
] }
parking_lot = { workspace = true }
anyhow = { workspace = true }
tokio = { workspace = true }
@@ -57,13 +65,6 @@ regex = "1.12.2"
sysproxy = { git = "https://github.com/clash-verge-rev/sysproxy-rs", features = [
"guard",
] }
tauri = { version = "2.9.3", features = [
"protocol-asset",
"devtools",
"tray-icon",
"image-ico",
"image-png",
] }
network-interface = { version = "2.0.3", features = ["serde"] }
tauri-plugin-shell = "2.3.3"
tauri-plugin-dialog = "2.4.2"
@@ -115,15 +116,6 @@ winapi = { version = "0.3.9", features = [
"winhttp",
"winreg",
] }
windows-sys = { version = "0.61.2", features = [
"Win32_Foundation",
"Win32_Graphics_Gdi",
"Win32_System_SystemServices",
"Win32_UI_WindowsAndMessaging",
] }
[target.'cfg(unix)'.dependencies]
signal-hook = "0.3.18"
[target.'cfg(not(any(target_os = "android", target_os = "ios")))'.dependencies]
tauri-plugin-autostart = "2.5.1"

View File

@@ -1,4 +1,3 @@
pub mod auto_backup;
pub mod lightweight;
pub mod signal;
pub mod sysinfo;

View File

@@ -1,12 +0,0 @@
#[cfg(unix)]
mod unix;
#[cfg(windows)]
mod windows;
pub fn register() {
#[cfg(windows)]
windows::register();
#[cfg(unix)]
unix::register();
}

View File

@@ -1,37 +0,0 @@
use signal_hook::{
consts::{SIGHUP, SIGINT, SIGTERM},
iterator::Signals,
low_level,
};
use crate::feat;
use clash_verge_logging::{Type, logging, logging_error};
pub fn register() {
tauri::async_runtime::spawn(async {
let signals = [SIGTERM, SIGINT, SIGHUP];
match Signals::new(signals) {
Ok(mut sigs) => {
for signal in &mut sigs {
let signal_to_str = |signal: i32| match signal {
SIGTERM => "SIGTERM",
SIGINT => "SIGINT",
SIGHUP => "SIGHUP",
_ => "UNKNOWN",
};
logging!(info, Type::System, "捕获到信号 {}", signal_to_str(signal));
feat::clean_async().await;
// After printing it, do whatever the signal was supposed to do in the first place
logging_error!(
Type::System,
"信号 {:?} 默认处理失败",
low_level::emulate_default_handler(signal)
);
}
}
Err(e) => {
logging!(error, Type::System, "注册信号处理器失败: {}", e);
}
}
});
}

View File

@@ -9,11 +9,13 @@ use crate::{
sysopt,
tray::Tray,
},
module::{auto_backup::AutoBackupManager, lightweight::auto_lightweight_boot, signal},
feat,
module::{auto_backup::AutoBackupManager, lightweight::auto_lightweight_boot},
process::AsyncHandler,
utils::{init, server, window_manager::WindowManager},
};
use clash_verge_logging::{Type, logging, logging_error};
use clash_verge_signal;
pub mod dns;
pub mod scheme;
@@ -134,7 +136,11 @@ pub(super) async fn init_auto_backup() {
pub(super) fn init_signal() {
logging!(info, Type::Setup, "Initializing signal handlers...");
signal::register();
clash_verge_signal::register(
#[cfg(windows)]
handle::Handle::app_handle(),
feat::clean_async,
);
}
pub async fn init_work_config() {