From 8bddf30dcf1603ad7eb0959d912e497d2bbff8db Mon Sep 17 00:00:00 2001
From: GyDi <segydi@foxmail.com>
Date: Fri, 23 Sep 2022 15:31:01 +0800
Subject: [PATCH] refactor(hotkey): use tauri global shortcut

---
 src-tauri/Cargo.lock                          |  46 --------
 src-tauri/Cargo.toml                          |   3 +-
 src-tauri/src/core/hotkey.rs                  | 100 +++++++++---------
 src-tauri/src/core/mod.rs                     |   4 +-
 src-tauri/tauri.conf.json                     |   3 +
 src/components/setting/mods/hotkey-viewer.tsx |   4 +-
 6 files changed, 58 insertions(+), 102 deletions(-)

diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock
index f51c078..3a8d1cd 100644
--- a/src-tauri/Cargo.lock
+++ b/src-tauri/Cargo.lock
@@ -539,7 +539,6 @@ dependencies = [
  "sysproxy",
  "tauri",
  "tauri-build",
- "tauri-hotkey",
  "tokio",
  "warp",
  "which 4.3.0",
@@ -3785,24 +3784,6 @@ 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.100"
@@ -4022,33 +4003,6 @@ 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.1.1"
diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml
index 6a8f6c6..6735c86 100644
--- a/src-tauri/Cargo.toml
+++ b/src-tauri/Cargo.toml
@@ -33,11 +33,10 @@ 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"] }
-tauri = { version = "1.1.1", features = ["process-all", "shell-all", "system-tray", "updater", "window-all"] }
+tauri = { version = "1.1.1", features = ["global-shortcut-all", "process-all", "shell-all", "system-tray", "updater", "window-all"] }
 rquickjs = { version = "0.1.7" }
 window-shadows = { version = "0.2.0" }
 window-vibrancy = { version = "0.3.0" }
diff --git a/src-tauri/src/core/hotkey.rs b/src-tauri/src/core/hotkey.rs
index 815a2e4..7446746 100644
--- a/src-tauri/src/core/hotkey.rs
+++ b/src-tauri/src/core/hotkey.rs
@@ -1,22 +1,23 @@
 use crate::{data::*, feat, log_if_err};
 use anyhow::{bail, Result};
 use std::collections::HashMap;
-use tauri_hotkey::{parse_hotkey, HotkeyManager};
+use tauri::{AppHandle, GlobalShortcutManager};
 
 pub struct Hotkey {
   current: Vec<String>, // 保存当前的热键设置
-  manager: HotkeyManager,
+  manager: Option<AppHandle>,
 }
 
 impl Hotkey {
   pub fn new() -> Hotkey {
     Hotkey {
       current: Vec::new(),
-      manager: HotkeyManager::new(),
+      manager: None,
     }
   }
 
-  pub fn init(&mut self) -> Result<()> {
+  pub fn init(&mut self, app_handle: AppHandle) -> Result<()> {
+    self.manager = Some(app_handle);
     let data = Data::global();
     let verge = data.verge.lock();
 
@@ -27,9 +28,9 @@ impl Hotkey {
         let key = iter.next();
 
         if func.is_some() && key.is_some() {
-          log_if_err!(self.register(func.unwrap(), key.unwrap()));
+          log_if_err!(self.register(key.unwrap(), func.unwrap()));
         } else {
-          log::error!(target: "app", "invalid hotkey \"{}\":\"{}\"", func.unwrap_or("None"), key.unwrap_or("None"));
+          log::error!(target: "app", "invalid hotkey \"{}\":\"{}\"", key.unwrap_or("None"), func.unwrap_or("None"));
         }
       }
       self.current = hotkeys.clone();
@@ -38,18 +39,25 @@ impl Hotkey {
     Ok(())
   }
 
-  fn register(&mut self, func: &str, key: &str) -> Result<()> {
-    let hotkey = parse_hotkey(key.trim())?;
+  fn get_manager(&self) -> Result<impl GlobalShortcutManager> {
+    if self.manager.is_none() {
+      bail!("failed to get hotkey manager");
+    }
+    Ok(self.manager.as_ref().unwrap().global_shortcut_manager())
+  }
+
+  fn register(&mut self, hotkey: &str, func: &str) -> Result<()> {
+    let mut manager = self.get_manager()?;
 
-    if self.manager.is_registered(&hotkey) {
-      self.manager.unregister(&hotkey)?;
+    if manager.is_registered(hotkey)? {
+      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"),
+      "clash_mode_direct" => || feat::change_clash_mode("direct"),
+      "clash_mode_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(),
@@ -60,15 +68,14 @@ impl Hotkey {
       _ => bail!("invalid function \"{func}\""),
     };
 
-    self.manager.register(hotkey, f)?;
-    log::info!(target: "app", "register hotkey {func} {key}");
+    manager.register(hotkey, f)?;
+    log::info!(target: "app", "register hotkey {hotkey} {func}");
     Ok(())
   }
 
-  fn unregister(&mut self, key: &str) -> Result<()> {
-    let hotkey = parse_hotkey(key.trim())?;
-    self.manager.unregister(&hotkey)?;
-    log::info!(target: "app", "unregister hotkey {key}");
+  fn unregister(&mut self, hotkey: &str) -> Result<()> {
+    self.get_manager()?.unregister(&hotkey)?;
+    log::info!(target: "app", "unregister hotkey {hotkey}");
     Ok(())
   }
 
@@ -77,20 +84,15 @@ impl Hotkey {
     let old_map = Self::get_map_from_vec(&current);
     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));
-        }
-      }
-    }
+    let (del, add) = Self::get_diff(old_map, new_map);
+
+    del.iter().for_each(|key| {
+      let _ = self.unregister(key);
+    });
+
+    add.iter().for_each(|(key, func)| {
+      log_if_err!(self.register(key, func));
+    });
 
     self.current = new_hotkeys;
     Ok(())
@@ -116,38 +118,36 @@ impl Hotkey {
   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![];
+  ) -> (Vec<&'a str>, Vec<(&'a str, &'a str)>) {
+    let mut del_list = vec![];
+    let mut add_list = vec![];
 
-    old_map
-      .iter()
-      .for_each(|(key, func)| match new_map.get(key) {
+    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));
+            del_list.push(key);
+            add_list.push((key, *new_func));
           }
         }
-        None => list.push(Diff::Del(key)),
-      });
+        None => del_list.push(key),
+      };
+    });
 
-    new_map.iter().for_each(|(key, func)| {
+    new_map.iter().for_each(|(&key, &func)| {
       if old_map.get(key).is_none() {
-        list.push(Diff::Add(key, func));
+        add_list.push((key, func));
       }
     });
 
-    list
+    (del_list, add_list)
   }
 }
 
 impl Drop for Hotkey {
   fn drop(&mut self) {
-    let _ = self.manager.unregister_all();
+    if let Ok(mut manager) = self.get_manager() {
+      let _ = 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 3d122b3..21293dc 100644
--- a/src-tauri/src/core/mod.rs
+++ b/src-tauri/src/core/mod.rs
@@ -49,7 +49,7 @@ impl Core {
     Service::kill_old_clash();
 
     let mut handle = self.handle.lock();
-    handle.set_inner(app_handle);
+    handle.set_inner(app_handle.clone());
     drop(handle);
 
     let mut service = self.service.lock();
@@ -69,7 +69,7 @@ impl Core {
     drop(handle);
 
     let mut hotkey = self.hotkey.lock();
-    log_if_err!(hotkey.init());
+    log_if_err!(hotkey.init(app_handle));
     drop(hotkey);
 
     // timer initialize
diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json
index 04e266b..b5205b9 100644
--- a/src-tauri/tauri.conf.json
+++ b/src-tauri/tauri.conf.json
@@ -68,6 +68,9 @@
       },
       "process": {
         "all": true
+      },
+      "globalShortcut": {
+        "all": true
       }
     },
     "windows": [
diff --git a/src/components/setting/mods/hotkey-viewer.tsx b/src/components/setting/mods/hotkey-viewer.tsx
index 8a66f4f..7cabed3 100644
--- a/src/components/setting/mods/hotkey-viewer.tsx
+++ b/src/components/setting/mods/hotkey-viewer.tsx
@@ -25,9 +25,9 @@ const ItemWrapper = styled("div")`
 
 const HOTKEY_FUNC = [
   "clash_mode_rule",
-  "clash_mode_direct",
   "clash_mode_global",
-  "clash_moda_script",
+  "clash_mode_direct",
+  "clash_mode_script",
   "toggle_system_proxy",
   "enable_system_proxy",
   "disable_system_proxy",
-- 
GitLab