From b2feaf3ba986863ce337b1de8f0731a7ca27c791 Mon Sep 17 00:00:00 2001
From: ShiinaRinne <45081750+ShiinaRinne@users.noreply.github.com>
Date: Thu, 30 Jan 2025 13:24:01 +0800
Subject: [PATCH] [macOS] feat: auto run.
 https://github.com/2dust/v2rayN/issues/6383 (#6597)

---
 .../ServiceLib/Handler/AutoStartupHandler.cs  | 84 ++++++++++++++++++-
 .../Views/OptionSettingWindow.axaml.cs        |  7 --
 2 files changed, 81 insertions(+), 10 deletions(-)

diff --git a/v2rayN/ServiceLib/Handler/AutoStartupHandler.cs b/v2rayN/ServiceLib/Handler/AutoStartupHandler.cs
index e6ac42d3b8a..2858ac232aa 100644
--- a/v2rayN/ServiceLib/Handler/AutoStartupHandler.cs
+++ b/v2rayN/ServiceLib/Handler/AutoStartupHandler.cs
@@ -1,4 +1,5 @@
-using System.Security.Principal;
+using System.Diagnostics;
+using System.Security.Principal;
 using System.Text.RegularExpressions;
 
 namespace ServiceLib.Handler
@@ -29,7 +30,12 @@ public static async Task<bool> UpdateTask(Config config)
             }
             else if (Utils.IsOSX())
             {
-                //TODO
+	            await ClearTaskOSX();
+
+	            if (config.GuiItem.AutoRun)
+	            {
+		            await SetTaskOSX();
+	            }
             }
 
             return true;
@@ -161,5 +167,77 @@ private static string GetHomePathLinux()
         }
 
         #endregion Linux
+
+        #region macOS
+
+        private static async Task ClearTaskOSX()
+        {
+	        try
+	        {
+		        var launchAgentPath = GetLaunchAgentPathMacOS();
+		        if (File.Exists(launchAgentPath))
+		        {
+			        var args = new[] { "-c", $"launchctl unload -w \"{launchAgentPath}\"" };
+			        await Utils.GetCliWrapOutput("/bin/bash", args);
+
+			        File.Delete(launchAgentPath);
+		        }
+	        }
+	        catch (Exception ex)
+	        {
+		        Logging.SaveLog(_tag, ex);
+	        }
+        }
+
+        private static async Task SetTaskOSX()
+        {
+	        try
+	        {
+		        var plistContent = GenerateLaunchAgentPlist();
+		        var launchAgentPath = GetLaunchAgentPathMacOS();
+		        await File.WriteAllTextAsync(launchAgentPath, plistContent);
+
+		        var args = new[] { "-c", $"launchctl load -w \"{launchAgentPath}\"" };
+		        await Utils.GetCliWrapOutput("/bin/bash", args);
+	        }
+	        catch (Exception ex)
+	        {
+		        Logging.SaveLog(_tag, ex);
+	        }
+        }
+
+        private static string GetLaunchAgentPathMacOS()
+        {
+	        var homePath = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile);
+	        var launchAgentPath = Path.Combine(homePath, "Library", "LaunchAgents", $"{Global.AppName}-LaunchAgent.plist");
+	        Directory.CreateDirectory(Path.GetDirectoryName(launchAgentPath));
+	        return launchAgentPath;
+        }
+
+        private static string GenerateLaunchAgentPlist()
+        {
+            var exePath = Utils.GetExePath();
+            var appName = Path.GetFileNameWithoutExtension(exePath);
+            return $@"<?xml version=""1.0"" encoding=""UTF-8""?>
+<!DOCTYPE plist PUBLIC ""-//Apple//DTD PLIST 1.0//EN"" ""http://www.apple.com/DTDs/PropertyList-1.0.dtd"">
+<plist version=""1.0"">
+<dict>
+    <key>Label</key>
+    <string>{Global.AppName}-LaunchAgent</string>
+    <key>ProgramArguments</key>
+    <array>
+        <string>/bin/sh</string>
+        <string>-c</string>
+        <string>if ! pgrep -x ""{appName}"" > /dev/null; then ""{exePath}""; fi</string>
+    </array>
+    <key>RunAtLoad</key>
+    <true/>
+    <key>KeepAlive</key>
+    <false/>
+</dict>
+</plist>";
+        }
+
+        #endregion macOS
     }
-}
\ No newline at end of file
+}
diff --git a/v2rayN/v2rayN.Desktop/Views/OptionSettingWindow.axaml.cs b/v2rayN/v2rayN.Desktop/Views/OptionSettingWindow.axaml.cs
index 35a85bd974d..ebc45bf5290 100644
--- a/v2rayN/v2rayN.Desktop/Views/OptionSettingWindow.axaml.cs
+++ b/v2rayN/v2rayN.Desktop/Views/OptionSettingWindow.axaml.cs
@@ -176,13 +176,6 @@ public OptionSettingWindow()
                 txbSettingsExceptionTip.IsVisible = false;
                 panSystemProxyAdvanced.IsVisible = false;
             }
-
-            if (Utils.IsOSX())
-            {
-                tbAutoRun.IsVisible = false;
-                togAutoRun.IsVisible = false;
-                //TODO
-            }
         }
 
         private async Task<bool> UpdateViewHandler(EViewAction action, object? obj)