From d7c5ce0750e69b9368e80da73fc4d796b683a0f5 Mon Sep 17 00:00:00 2001
From: GyDi <segydi@foxmail.com>
Date: Fri, 25 Feb 2022 02:09:39 +0800
Subject: [PATCH] feat: clash tun mode supports

---
 src-tauri/src/cmds.rs                     | 18 +++++++++-
 src-tauri/src/core/clash.rs               | 42 +++++++++++++++++++++++
 src-tauri/src/core/verge.rs               |  8 +++++
 src-tauri/src/utils/resolve.rs            | 15 ++++++--
 src/components/setting/setting-system.tsx | 15 ++++++++
 src/services/types.ts                     |  1 +
 6 files changed, 95 insertions(+), 4 deletions(-)

diff --git a/src-tauri/src/cmds.rs b/src-tauri/src/cmds.rs
index 8b2a6b5..2a7c87c 100644
--- a/src-tauri/src/cmds.rs
+++ b/src-tauri/src/cmds.rs
@@ -259,10 +259,26 @@ pub fn get_verge_config(verge_state: State<'_, VergeState>) -> Result<VergeConfi
 #[tauri::command]
 pub fn patch_verge_config(
   payload: VergeConfig,
+  clash_state: State<'_, ClashState>,
   verge_state: State<'_, VergeState>,
+  profiles_state: State<'_, ProfilesState>,
 ) -> Result<(), String> {
+  let tun_mode = payload.enable_tun_mode.clone();
+
   let mut verge = verge_state.0.lock().unwrap();
-  verge.patch_config(payload)
+  verge.patch_config(payload)?;
+
+  // change tun mode
+  if tun_mode.is_some() {
+    let mut clash = clash_state.0.lock().unwrap();
+    let profiles = profiles_state.0.lock().unwrap();
+
+    clash.tun_mode(tun_mode.unwrap())?;
+    clash.update_config();
+    profiles.activate(&clash)?;
+  }
+
+  Ok(())
 }
 
 /// kill all sidecars when update app
diff --git a/src-tauri/src/core/clash.rs b/src-tauri/src/core/clash.rs
index c7a23a9..80ea316 100644
--- a/src-tauri/src/core/clash.rs
+++ b/src-tauri/src/core/clash.rs
@@ -203,6 +203,48 @@ impl Clash {
     }
     self.save_config()
   }
+
+  /// enable tun mode
+  /// only revise the config and restart the
+  pub fn tun_mode(&mut self, enable: bool) -> Result<(), String> {
+    let tun_key = Value::String("tun".into());
+    let tun_val = self.config.get(&tun_key);
+
+    let mut new_val = Mapping::new();
+
+    if tun_val.is_some() && tun_val.as_ref().unwrap().is_mapping() {
+      new_val = tun_val.as_ref().unwrap().as_mapping().unwrap().clone();
+    }
+
+    macro_rules! revise {
+      ($map: expr, $key: expr, $val: expr) => {
+        let ret_key = Value::String($key.into());
+        if $map.contains_key(&ret_key) {
+          $map[&ret_key] = $val;
+        } else {
+          $map.insert(ret_key, $val);
+        }
+      };
+    }
+
+    macro_rules! append {
+      ($map: expr, $key: expr, $val: expr) => {
+        let ret_key = Value::String($key.into());
+        if !$map.contains_key(&ret_key) {
+          $map.insert(ret_key, $val);
+        }
+      };
+    }
+
+    revise!(new_val, "enable", Value::from(enable));
+    append!(new_val, "stack", Value::from("gvisor"));
+    append!(new_val, "auto-route", Value::from(true));
+    append!(new_val, "auto-detect-interface", Value::from(true));
+
+    revise!(self.config, "tun", Value::from(new_val));
+
+    self.save_config()
+  }
 }
 
 impl Default for Clash {
diff --git a/src-tauri/src/core/verge.rs b/src-tauri/src/core/verge.rs
index c6959fb..349480a 100644
--- a/src-tauri/src/core/verge.rs
+++ b/src-tauri/src/core/verge.rs
@@ -20,6 +20,9 @@ pub struct VergeConfig {
   /// enable traffic graph default is true
   pub traffic_graph: Option<bool>,
 
+  /// clash tun mode
+  pub enable_tun_mode: Option<bool>,
+
   /// can the app auto startup
   pub enable_auto_launch: Option<bool>,
 
@@ -258,6 +261,11 @@ impl Verge {
       Verge::guard_proxy(self.guard_state.clone());
     }
 
+    // handle the tun mode
+    if patch.enable_tun_mode.is_some() {
+      self.config.enable_tun_mode = patch.enable_tun_mode;
+    }
+
     self.config.save_file()
   }
 }
diff --git a/src-tauri/src/utils/resolve.rs b/src-tauri/src/utils/resolve.rs
index bb1cc6a..9fe90ce 100644
--- a/src-tauri/src/utils/resolve.rs
+++ b/src-tauri/src/utils/resolve.rs
@@ -22,18 +22,27 @@ pub fn resolve_setup(app: &App) {
   let mut profiles = profiles_state.0.lock().unwrap();
 
   if let Err(err) = clash.run_sidecar() {
-    log::error!("{}", err);
+    log::error!("{err}");
   }
 
   *profiles = Profiles::read_file();
   if let Err(err) = profiles.activate(&clash) {
-    log::error!("{}", err);
+    log::error!("{err}");
   }
 
   verge.init_sysproxy(clash.info.port.clone());
+  // enable tun mode
+  if verge.config.enable_tun_mode.clone().unwrap_or(false)
+    && verge.cur_sysproxy.is_some()
+    && verge.cur_sysproxy.as_ref().unwrap().enable
+  {
+    log::info!("enable tun mode");
+    clash.tun_mode(true).unwrap();
+  }
+
   verge.init_launch();
   if let Err(err) = verge.sync_launch() {
-    log::error!("{}", err);
+    log::error!("{err}");
   }
 }
 
diff --git a/src/components/setting/setting-system.tsx b/src/components/setting/setting-system.tsx
index d7db114..dfc482e 100644
--- a/src/components/setting/setting-system.tsx
+++ b/src/components/setting/setting-system.tsx
@@ -15,6 +15,7 @@ const SettingSystem = ({ onError }: Props) => {
   const { data: vergeConfig } = useSWR("getVergeConfig", getVergeConfig);
 
   const {
+    enable_tun_mode = false,
     enable_auto_launch = false,
     enable_system_proxy = false,
     system_proxy_bypass = "",
@@ -28,6 +29,20 @@ const SettingSystem = ({ onError }: Props) => {
 
   return (
     <SettingList title="System Setting">
+      <SettingItem>
+        <ListItemText primary="Tun Mode" />
+        <GuardState
+          value={enable_tun_mode}
+          valueProps="checked"
+          onCatch={onError}
+          onFormat={onSwitchFormat}
+          onChange={(e) => onChangeData({ enable_tun_mode: e })}
+          onGuard={(e) => patchVergeConfig({ enable_tun_mode: e })}
+        >
+          <Switch edge="end" />
+        </GuardState>
+      </SettingItem>
+
       <SettingItem>
         <ListItemText primary="Auto Launch" />
         <GuardState
diff --git a/src/services/types.ts b/src/services/types.ts
index 80eb099..8843766 100644
--- a/src/services/types.ts
+++ b/src/services/types.ts
@@ -113,6 +113,7 @@ export namespace CmdType {
     theme_mode?: "light" | "dark";
     theme_blur?: boolean;
     traffic_graph?: boolean;
+    enable_tun_mode?: boolean;
     enable_auto_launch?: boolean;
     enable_system_proxy?: boolean;
     enable_proxy_guard?: boolean;
-- 
GitLab