From cb816e9653b71a5cade2193da489fa973dc2d511 Mon Sep 17 00:00:00 2001
From: GyDi <segydi@foxmail.com>
Date: Fri, 31 Dec 2021 18:24:50 +0800
Subject: [PATCH] feat: support restart sidecar tray event

---
 src-tauri/src/cmds/some.rs     | 22 +++++++++++++++++-----
 src-tauri/src/events/state.rs  |  7 +++++--
 src-tauri/src/main.rs          | 22 +++++++++++++++++++++-
 src-tauri/src/utils/clash.rs   | 26 ++++++++++++++++++++------
 src-tauri/src/utils/resolve.rs |  9 +--------
 5 files changed, 64 insertions(+), 22 deletions(-)

diff --git a/src-tauri/src/cmds/some.rs b/src-tauri/src/cmds/some.rs
index 291be68..44a89dc 100644
--- a/src-tauri/src/cmds/some.rs
+++ b/src-tauri/src/cmds/some.rs
@@ -2,7 +2,7 @@ use crate::{
   config::VergeConfig,
   events::{
     emit::ClashInfoPayload,
-    state::{ClashInfoState, VergeConfLock},
+    state::{ClashInfoState, ClashSidecarState, VergeConfLock},
   },
   utils::{
     clash::run_clash_bin,
@@ -11,13 +11,25 @@ use crate::{
   },
 };
 use serde_yaml::Mapping;
-use tauri::{api::process::kill_children, AppHandle, State};
+use tauri::{AppHandle, State};
 
 /// restart the sidecar
 #[tauri::command]
-pub fn restart_sidecar(app_handle: AppHandle, clash_info: State<'_, ClashInfoState>) {
-  kill_children();
-  let payload = run_clash_bin(&app_handle, |_| {});
+pub fn restart_sidecar(
+  app_handle: AppHandle,
+  clash_info: State<'_, ClashInfoState>,
+  clash_sidecar: State<'_, ClashSidecarState>,
+) {
+  {
+    let mut guard = clash_sidecar.0.lock().unwrap();
+    let sidecar = guard.take();
+    if sidecar.is_some() {
+      if let Err(err) = sidecar.unwrap().kill() {
+        log::error!("failed to restart clash for \"{}\"", err);
+      }
+    }
+  }
+  let payload = run_clash_bin(&app_handle);
 
   if let Ok(mut arc) = clash_info.0.lock() {
     *arc = payload;
diff --git a/src-tauri/src/events/state.rs b/src-tauri/src/events/state.rs
index bcdd54e..01b1b30 100644
--- a/src-tauri/src/events/state.rs
+++ b/src-tauri/src/events/state.rs
@@ -1,7 +1,7 @@
-use std::sync::{Arc, Mutex};
-
 use super::emit::ClashInfoPayload;
 use crate::{config::VergeConfig, utils::sysopt::SysProxyConfig};
+use std::sync::{Arc, Mutex};
+use tauri::api::process::CommandChild;
 
 #[derive(Default)]
 pub struct ClashInfoState(pub Arc<Mutex<ClashInfoPayload>>);
@@ -14,3 +14,6 @@ pub struct VergeConfLock(pub Arc<Mutex<VergeConfig>>);
 
 #[derive(Default)]
 pub struct SomthingState(pub Arc<Mutex<Option<SysProxyConfig>>>);
+
+#[derive(Default)]
+pub struct ClashSidecarState(pub Arc<Mutex<Option<CommandChild>>>);
diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs
index ef465ad..c425403 100644
--- a/src-tauri/src/main.rs
+++ b/src-tauri/src/main.rs
@@ -12,7 +12,7 @@ mod utils;
 
 use crate::{
   events::state,
-  utils::{resolve, server},
+  utils::{clash, resolve, server},
 };
 use tauri::{
   api, CustomMenuItem, Manager, SystemTray, SystemTrayEvent, SystemTrayMenu, SystemTrayMenuItem,
@@ -26,10 +26,12 @@ fn main() -> std::io::Result<()> {
 
   let menu = SystemTrayMenu::new()
     .add_item(CustomMenuItem::new("open_window", "显示应用"))
+    .add_item(CustomMenuItem::new("restart_clash", "重启clash"))
     .add_native_item(SystemTrayMenuItem::Separator)
     .add_item(CustomMenuItem::new("quit", "退出").accelerator("CmdOrControl+Q"));
 
   tauri::Builder::default()
+    .manage(state::ClashSidecarState::default())
     .manage(state::VergeConfLock::default())
     .manage(state::ClashInfoState::default())
     .manage(state::SomthingState::default())
@@ -43,6 +45,24 @@ fn main() -> std::io::Result<()> {
           window.show().unwrap();
           window.set_focus().unwrap();
         }
+        "restart_clash" => {
+          {
+            let state = app_handle.state::<state::ClashSidecarState>();
+            let mut guard = state.0.lock().unwrap();
+            let sidecar = guard.take();
+            if sidecar.is_some() {
+              if let Err(err) = sidecar.unwrap().kill() {
+                log::error!("failed to restart clash for \"{}\"", err);
+              }
+            }
+          }
+
+          let payload = clash::run_clash_bin(&app_handle);
+          let state = app_handle.state::<state::ClashInfoState>();
+          if let Ok(mut arc) = state.0.lock() {
+            *arc = payload;
+          };
+        }
         "quit" => {
           api::process::kill_children();
           resolve::resolve_reset(app_handle);
diff --git a/src-tauri/src/utils/clash.rs b/src-tauri/src/utils/clash.rs
index 010882e..77252ac 100644
--- a/src-tauri/src/utils/clash.rs
+++ b/src-tauri/src/utils/clash.rs
@@ -1,9 +1,12 @@
 extern crate log;
 
 use crate::{
-  events::emit::{clash_start, ClashInfoPayload},
+  events::{
+    emit::{clash_start, ClashInfoPayload},
+    state,
+  },
   utils::{
-    app_home_dir,
+    app_home_dir, clash,
     config::{read_clash_controller, read_profiles, read_yaml, save_yaml},
   },
 };
@@ -12,11 +15,11 @@ use serde_yaml::{Mapping, Value};
 use std::{collections::HashMap, env::temp_dir};
 use tauri::{
   api::process::{Command, CommandEvent},
-  AppHandle,
+  AppHandle, Manager,
 };
 
 /// Run the clash bin
-pub fn run_clash_bin(app_handle: &AppHandle, cb: fn(info: ClashInfoPayload)) -> ClashInfoPayload {
+pub fn run_clash_bin(app_handle: &AppHandle) -> ClashInfoPayload {
   let app_dir = app_home_dir();
   let app_dir = app_dir.as_os_str().to_str().unwrap();
 
@@ -35,10 +38,21 @@ pub fn run_clash_bin(app_handle: &AppHandle, cb: fn(info: ClashInfoPayload)) ->
   };
 
   match result {
-    Ok((mut rx, _)) => {
+    Ok((mut rx, cmd_child)) => {
       log::info!("Successfully execute clash sidecar");
       payload.controller = Some(read_clash_controller());
-      cb(payload.clone()); // callback when run sidecar successfully
+
+      // update the profile
+      let payload_ = payload.clone();
+      tauri::async_runtime::spawn(async move {
+        if let Err(err) = clash::put_clash_profile(&payload_).await {
+          log::error!("failed to put config for `{}`", err);
+        };
+      });
+
+      if let Ok(mut state) = app_handle.state::<state::ClashSidecarState>().0.lock() {
+        *state = Some(cmd_child);
+      };
 
       tauri::async_runtime::spawn(async move {
         while let Some(event) = rx.recv().await {
diff --git a/src-tauri/src/utils/resolve.rs b/src-tauri/src/utils/resolve.rs
index 5cd1606..8c85853 100644
--- a/src-tauri/src/utils/resolve.rs
+++ b/src-tauri/src/utils/resolve.rs
@@ -11,14 +11,7 @@ pub fn resolve_setup(app: &App) {
   init::init_app(app.package_info());
 
   // run clash sidecar
-  let info = clash::run_clash_bin(&app.handle(), |info_| {
-    // update the profile
-    tauri::async_runtime::spawn(async move {
-      if let Err(err) = clash::put_clash_profile(&info_).await {
-        log::error!("failed to put config for `{}`", err);
-      };
-    });
-  });
+  let info = clash::run_clash_bin(&app.handle());
 
   // resolve the verge config - enable system proxy
   let mut original: Option<sysopt::SysProxyConfig> = None;
-- 
GitLab