diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index 5accaced8748df5ceeae22256008054dea1d1e85..ccecbbf90951f4e57abae88c6286f6d1430f294f 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -526,6 +526,7 @@ dependencies = [ "sysproxy", "tauri", "tauri-build", + "tauri-hotkey", "tokio", "warp", "which 4.2.5", @@ -3819,6 +3820,24 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" +[[package]] +name = "strum" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7318c509b5ba57f18533982607f24070a55d353e90d4cae30c467cdb2ad5ac5c" + +[[package]] +name = "strum_macros" +version = "0.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee8bc6b87a5112aeeab1f4a9f7ab634fe6cbefc4850006df31267f4cfb9e3149" +dependencies = [ + "heck 0.3.3", + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "syn" version = "1.0.98" @@ -4038,6 +4057,33 @@ dependencies = [ "walkdir", ] +[[package]] +name = "tauri-hotkey" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c6cf71018e75b7c88f0c9643329891668c32cb377a1cccdd1f2973f51eff118" +dependencies = [ + "log 0.4.17", + "once_cell", + "serde", + "strum", + "strum_macros", + "tauri-hotkey-sys", + "thiserror", +] + +[[package]] +name = "tauri-hotkey-sys" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7024154106177cefd2592bcb0bb3df9dd3aea8a7e21f8fefb8d5b02fe115fed7" +dependencies = [ + "cc", + "thiserror", + "winapi", + "x11-dl", +] + [[package]] name = "tauri-macros" version = "1.0.4" diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index 852daefa13d10ba4df6c1887e51bbcdfd5ef4040..5f1058026bab4ad703b734d236a0b0cc7569142c 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -33,6 +33,7 @@ once_cell = "1.14.0" port_scanner = "0.1.5" delay_timer = "0.11.1" parking_lot = "0.12.0" +tauri-hotkey = "0.1.2" tokio = { version = "1", features = ["full"] } serde = { version = "1.0", features = ["derive"] } reqwest = { version = "0.11", features = ["json"] } diff --git a/src-tauri/src/cmds.rs b/src-tauri/src/cmds.rs index 16d81921213d00b322c175122b227037504fea11..27892d501384c31ac9d95adcf3220c80645bc038 100644 --- a/src-tauri/src/cmds.rs +++ b/src-tauri/src/cmds.rs @@ -213,7 +213,6 @@ pub fn patch_clash_config(payload: Mapping) -> CmdResult { wrap_err!(core.patch_clash(payload)) } -/// get the verge config #[tauri::command] pub fn get_verge_config() -> CmdResult<Verge> { let global = Data::global(); @@ -229,6 +228,13 @@ pub fn patch_verge_config(payload: Verge) -> CmdResult { wrap_err!(core.patch_verge(payload)) } +#[tauri::command] +pub fn update_hotkeys(hotkeys: Vec<String>) -> CmdResult { + let core = Core::global(); + let mut hotkey = core.hotkey.lock(); + wrap_err!(hotkey.update(hotkeys)) +} + /// change clash core #[tauri::command] pub fn change_clash_core(clash_core: Option<String>) -> CmdResult { diff --git a/src-tauri/src/core/hotkey.rs b/src-tauri/src/core/hotkey.rs new file mode 100644 index 0000000000000000000000000000000000000000..d0239b8194c8d0dd959df0e818d4ff276e55dd1e --- /dev/null +++ b/src-tauri/src/core/hotkey.rs @@ -0,0 +1,157 @@ +use crate::{data::*, feat, log_if_err}; +use anyhow::{bail, Result}; +use std::collections::HashMap; +use tauri_hotkey::{parse_hotkey, HotkeyManager}; + +pub struct Hotkey { + manager: HotkeyManager, +} + +impl Hotkey { + pub fn new() -> Hotkey { + Hotkey { + manager: HotkeyManager::new(), + } + } + + pub fn init(&mut self) -> Result<()> { + let data = Data::global(); + let verge = data.verge.lock(); + + if let Some(hotkeys) = verge.hotkeys.as_ref() { + for hotkey in hotkeys.iter() { + let mut iter = hotkey.split(','); + let func = iter.next(); + let key = iter.next(); + + if func.is_some() && key.is_some() { + log_if_err!(self.register(func.unwrap(), key.unwrap())); + } else { + log::error!(target: "app", "invalid hotkey \"{}\":\"{}\"", func.unwrap_or("None"), key.unwrap_or("None")); + } + } + } + + Ok(()) + } + + fn register(&mut self, func: &str, key: &str) -> Result<()> { + let hotkey = parse_hotkey(key.trim())?; + + if self.manager.is_registered(&hotkey) { + self.manager.unregister(&hotkey)?; + } + + let f = match func.trim() { + "clash_mode_rule" => || feat::change_clash_mode("rule"), + "clash_mode_direct" => || feat::change_clash_mode("direct"), + "clash_mode_global" => || feat::change_clash_mode("global"), + "clash_moda_script" => || feat::change_clash_mode("script"), + "toggle_system_proxy" => || feat::toggle_system_proxy(), + "enable_system_proxy" => || feat::enable_system_proxy(), + "disable_system_proxy" => || feat::disable_system_proxy(), + "toggle_tun_mode" => || feat::toggle_tun_mode(), + "enable_tun_mode" => || feat::enable_tun_mode(), + "disable_tun_mode" => || feat::disable_tun_mode(), + + _ => bail!("invalid function \"{func}\""), + }; + + self.manager.register(hotkey, f)?; + Ok(()) + } + + fn unregister(&mut self, key: &str) -> Result<()> { + let hotkey = parse_hotkey(key.trim())?; + self.manager.unregister(&hotkey)?; + Ok(()) + } + + pub fn update(&mut self, new_hotkeys: Vec<String>) -> Result<()> { + let data = Data::global(); + let mut verge = data.verge.lock(); + + let default = Vec::new(); + let old_hotkeys = verge.hotkeys.as_ref().unwrap_or(&default); + + let old_map = Self::get_map_from_vec(old_hotkeys); + let new_map = Self::get_map_from_vec(&new_hotkeys); + + for diff in Self::get_diff(old_map, new_map).iter() { + match diff { + Diff::Del(key) => { + let _ = self.unregister(key); + } + Diff::Mod(key, func) => { + let _ = self.unregister(key); + log_if_err!(self.register(func, key)); + } + Diff::Add(key, func) => { + log_if_err!(self.register(func, key)); + } + } + } + + verge.patch_config(Verge { + hotkeys: Some(new_hotkeys), + ..Verge::default() + })?; + + Ok(()) + } + + fn get_map_from_vec<'a>(hotkeys: &'a Vec<String>) -> HashMap<&'a str, &'a str> { + let mut map = HashMap::new(); + + hotkeys.iter().for_each(|hotkey| { + let mut iter = hotkey.split(','); + let func = iter.next(); + let key = iter.next(); + + if func.is_some() && key.is_some() { + let func = func.unwrap().trim(); + let key = key.unwrap().trim(); + map.insert(key, func); + } + }); + map + } + + fn get_diff<'a>( + old_map: HashMap<&'a str, &'a str>, + new_map: HashMap<&'a str, &'a str>, + ) -> Vec<Diff<'a>> { + let mut list = vec![]; + + old_map + .iter() + .for_each(|(key, func)| match new_map.get(key) { + Some(new_func) => { + if new_func != func { + list.push(Diff::Mod(key, new_func)); + } + } + None => list.push(Diff::Del(key)), + }); + + new_map.iter().for_each(|(key, func)| { + if old_map.get(key).is_none() { + list.push(Diff::Add(key, func)); + } + }); + + list + } +} + +impl Drop for Hotkey { + fn drop(&mut self) { + let _ = self.manager.unregister_all(); + } +} + +enum Diff<'a> { + Del(&'a str), // key + Add(&'a str, &'a str), // key, func + Mod(&'a str, &'a str), // key, func +} diff --git a/src-tauri/src/core/mod.rs b/src-tauri/src/core/mod.rs index 24421f6bf717ccf40bd674650c2fe2daddfedae2..e5c162f02197767c63f0f26c0e8ad9268bb6e483 100644 --- a/src-tauri/src/core/mod.rs +++ b/src-tauri/src/core/mod.rs @@ -1,4 +1,5 @@ use self::handle::Handle; +use self::hotkey::Hotkey; use self::sysopt::Sysopt; use self::timer::Timer; use crate::config::enhance_config; @@ -11,6 +12,7 @@ use serde_yaml::{Mapping, Value}; use std::sync::Arc; mod handle; +mod hotkey; mod service; mod sysopt; mod timer; @@ -21,6 +23,7 @@ static CORE: Lazy<Core> = Lazy::new(|| Core { service: Arc::new(Mutex::new(Service::new())), sysopt: Arc::new(Mutex::new(Sysopt::new())), timer: Arc::new(Mutex::new(Timer::new())), + hotkey: Arc::new(Mutex::new(Hotkey::new())), runtime: Arc::new(Mutex::new(RuntimeResult::default())), handle: Arc::new(Mutex::new(Handle::default())), }); @@ -30,6 +33,7 @@ pub struct Core { pub service: Arc<Mutex<Service>>, pub sysopt: Arc<Mutex<Sysopt>>, pub timer: Arc<Mutex<Timer>>, + pub hotkey: Arc<Mutex<Hotkey>>, pub runtime: Arc<Mutex<RuntimeResult>>, pub handle: Arc<Mutex<Handle>>, } @@ -44,29 +48,29 @@ impl Core { // kill old clash process Service::kill_old_clash(); - { - let mut handle = self.handle.lock(); - handle.set_inner(app_handle); - } + let mut handle = self.handle.lock(); + handle.set_inner(app_handle); + drop(handle); - { - let mut service = self.service.lock(); - log_if_err!(service.start()); - } + let mut service = self.service.lock(); + log_if_err!(service.start()); + drop(service); log_if_err!(self.activate()); - { - let mut sysopt = self.sysopt.lock(); - log_if_err!(sysopt.init_launch()); - log_if_err!(sysopt.init_sysproxy()); - } + let mut sysopt = self.sysopt.lock(); + log_if_err!(sysopt.init_launch()); + log_if_err!(sysopt.init_sysproxy()); + drop(sysopt); - { - let handle = self.handle.lock(); - log_if_err!(handle.update_systray()); - log_if_err!(handle.update_systray_clash()); - } + let handle = self.handle.lock(); + log_if_err!(handle.update_systray()); + log_if_err!(handle.update_systray_clash()); + drop(handle); + + let mut hotkey = self.hotkey.lock(); + log_if_err!(hotkey.init()); + drop(hotkey); // timer initialize let mut timer = self.timer.lock(); diff --git a/src-tauri/src/data/verge.rs b/src-tauri/src/data/verge.rs index 98741db32bf9d2b5fec7f11c685b35bb718eb856..90aa5b76adb478890b92476636e75f1cc62a1b40 100644 --- a/src-tauri/src/data/verge.rs +++ b/src-tauri/src/data/verge.rs @@ -56,6 +56,10 @@ pub struct Verge { /// clash core path #[serde(skip_serializing_if = "Option::is_none")] pub clash_core: Option<String>, + + /// hotkey map + /// format: {func},{key} + pub hotkeys: Option<Vec<String>>, } #[derive(Default, Debug, Clone, Deserialize, Serialize)] @@ -116,6 +120,7 @@ impl Verge { patch!(theme_setting); patch!(web_ui_list); patch!(clash_core); + patch!(hotkeys); self.save_file() } diff --git a/src-tauri/src/feat.rs b/src-tauri/src/feat.rs new file mode 100644 index 0000000000000000000000000000000000000000..4b9bc4c25c0441123784df6a6076b01f854b9e0c --- /dev/null +++ b/src-tauri/src/feat.rs @@ -0,0 +1,75 @@ +use crate::core::*; +use crate::data::*; +use crate::log_if_err; + +// 切æ¢æ¨¡å¼ +pub fn change_clash_mode(mode: &str) { + let core = Core::global(); + log_if_err!(core.update_mode(mode)); +} + +// 切æ¢ç³»ç»Ÿä»£ç† +pub fn toggle_system_proxy() { + let core = Core::global(); + let data = Data::global(); + + let verge = data.verge.lock(); + let enable = !verge.enable_system_proxy.clone().unwrap_or(false); + drop(verge); + + log_if_err!(core.patch_verge(Verge { + enable_system_proxy: Some(enable), + ..Verge::default() + })); +} + +// æ‰“å¼€ç³»ç»Ÿä»£ç† +pub fn enable_system_proxy() { + let core = Core::global(); + log_if_err!(core.patch_verge(Verge { + enable_system_proxy: Some(true), + ..Verge::default() + })); +} + +// å…³é—ç³»ç»Ÿä»£ç† +pub fn disable_system_proxy() { + let core = Core::global(); + log_if_err!(core.patch_verge(Verge { + enable_system_proxy: Some(false), + ..Verge::default() + })); +} + +// 切æ¢tunæ¨¡å¼ +pub fn toggle_tun_mode() { + let core = Core::global(); + let data = Data::global(); + + let verge = data.verge.lock(); + let enable = !verge.enable_tun_mode.clone().unwrap_or(false); + drop(verge); + + log_if_err!(core.patch_verge(Verge { + enable_tun_mode: Some(enable), + ..Verge::default() + })); +} + +// 打开tunæ¨¡å¼ +pub fn enable_tun_mode() { + let core = Core::global(); + log_if_err!(core.patch_verge(Verge { + enable_tun_mode: Some(true), + ..Verge::default() + })); +} + +// å…³é—tunæ¨¡å¼ +pub fn disable_tun_mode() { + let core = Core::global(); + log_if_err!(core.patch_verge(Verge { + enable_tun_mode: Some(false), + ..Verge::default() + })); +} diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs index 0d0c04ff2c5c5eee1425ebc7f674cb30b98cbe08..1a928451b6d290f8d10e1b0e4db7fae4b0a8229d 100644 --- a/src-tauri/src/main.rs +++ b/src-tauri/src/main.rs @@ -7,11 +7,11 @@ mod cmds; mod config; mod core; mod data; +mod feat; mod utils; use crate::{ - core::Core, - data::{Data, Verge}, + data::Verge, utils::{resolve, server}, }; use tauri::{ @@ -66,45 +66,10 @@ fn main() -> std::io::Result<()> { } mode @ ("rule_mode" | "global_mode" | "direct_mode" | "script_mode") => { let mode = &mode[0..mode.len() - 5]; - let core = Core::global(); - crate::log_if_err!(core.update_mode(mode)); - } - "system_proxy" => { - let core = Core::global(); - - let new_value = { - let global = Data::global(); - let verge = global.verge.lock(); - !verge.enable_system_proxy.clone().unwrap_or(false) - }; - - let patch = Verge { - enable_system_proxy: Some(new_value), - ..Verge::default() - }; - - crate::log_if_err!(core.patch_verge(patch)); - } - "tun_mode" => { - let core = Core::global(); - - let new_value = { - let global = Data::global(); - let verge = global.verge.lock(); - !verge.enable_tun_mode.clone().unwrap_or(false) - }; - - let patch = Verge { - enable_tun_mode: Some(new_value), - ..Verge::default() - }; - - crate::log_if_err!(core.patch_verge(patch)); - } - "restart_clash" => { - let core = Core::global(); - crate::log_if_err!(core.restart_clash()); + feat::change_clash_mode(mode); } + "system_proxy" => feat::toggle_system_proxy(), + "tun_mode" => feat::toggle_tun_mode(), "restart_app" => { api::process::restart(&app_handle.env()); } @@ -140,6 +105,7 @@ fn main() -> std::io::Result<()> { // verge cmds::get_verge_config, cmds::patch_verge_config, + cmds::update_hotkeys, // profile cmds::view_profile, cmds::patch_profile, @@ -188,7 +154,7 @@ fn main() -> std::io::Result<()> { resolve::resolve_reset(); app_handle.exit(0); }) - .expect("error when exiting."); + .expect("error while exiting."); #[allow(unused)] app.run(|app_handle, e| match e {