diff --git a/StabilityMatrix.Avalonia/Models/AppArgs.cs b/StabilityMatrix.Avalonia/Models/AppArgs.cs index 30bc391b..236aa8dc 100644 --- a/StabilityMatrix.Avalonia/Models/AppArgs.cs +++ b/StabilityMatrix.Avalonia/Models/AppArgs.cs @@ -1,4 +1,7 @@ -namespace StabilityMatrix.Avalonia.Models; +using System; +using CommandLine; + +namespace StabilityMatrix.Avalonia.Models; /// /// Command line arguments passed to the application. @@ -8,41 +11,70 @@ public class AppArgs /// /// Whether to enable debug mode /// + [Option("debug", HelpText = "Enable debug mode")] public bool DebugMode { get; set; } /// /// Whether to use the exception dialog while debugger is attached. /// When no debugger is attached, the exception dialog is always used. /// + [Option("debug-exception-dialog", HelpText = "Use exception dialog while debugger is attached")] public bool DebugExceptionDialog { get; set; } /// /// Whether to use Sentry when a debugger is attached. /// + [Option("debug-sentry", HelpText = "Use Sentry when debugger is attached")] public bool DebugSentry { get; set; } /// /// Whether to force show the one-click install dialog. /// + [Option("debug-one-click-install", HelpText = "Force show the one-click install dialog")] public bool DebugOneClickInstall { get; set; } /// /// Whether to disable Sentry. /// + [Option("no-sentry", HelpText = "Disable Sentry")] public bool NoSentry { get; set; } /// /// Whether to disable window chrome effects /// + [Option("no-window-chrome-effects", HelpText = "Disable window chrome effects")] public bool NoWindowChromeEffects { get; set; } /// /// Flag to indicate if we should reset the saved window position back to (O,0) /// + [Option("reset-window-position", HelpText = "Reset the saved window position back to (0,0)")] public bool ResetWindowPosition { get; set; } /// /// Flag for disabling hardware acceleration / GPU rendering /// + [Option("disable-gpu-rendering", HelpText = "Disable hardware acceleration / GPU rendering")] public bool DisableGpuRendering { get; set; } + + /// + /// Override global app data directory + /// Defaults to (%APPDATA%|~/.config)/StabilityMatrix + /// + [Option("global-dir", HelpText = "Override global app data directory")] + public string? GlobalDirectory { get; set; } + + /// + /// Override data directory + /// This takes precedence over relative portable directory and global directory + /// + [Option("data-dir", HelpText = "Override data directory")] + public string? DataDirectory { get; set; } + + /// + /// Custom Uri protocol handler + /// This will send the Uri to the running instance of the app via IPC and exit + /// + [Option("uri", Hidden = true)] + public string? Uri { get; set; } } diff --git a/StabilityMatrix.Avalonia/Program.cs b/StabilityMatrix.Avalonia/Program.cs index 229c605b..8dbe9e07 100644 --- a/StabilityMatrix.Avalonia/Program.cs +++ b/StabilityMatrix.Avalonia/Program.cs @@ -2,7 +2,6 @@ using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.IO; -using System.Linq; using System.Reflection; using System.Runtime.InteropServices; using System.Threading; @@ -13,7 +12,7 @@ using Avalonia; using Avalonia.Controls; using Avalonia.Controls.ApplicationLifetimes; using Avalonia.Threading; -using FluentAvalonia.Core; +using CommandLine; using NLog; using Polly.Contrib.WaitAndRetry; using Projektanker.Icons.Avalonia; @@ -29,14 +28,12 @@ using StabilityMatrix.Core.Updater; namespace StabilityMatrix.Avalonia; -[SuppressMessage("ReSharper", "ClassNeverInstantiated.Global")] -[SuppressMessage("ReSharper", "MemberCanBePrivate.Global")] -public class Program +public static class Program { private static Logger? _logger; private static Logger Logger => _logger ??= LogManager.GetCurrentClassLogger(); - public static AppArgs Args { get; } = new(); + public static AppArgs Args { get; private set; } = new(); public static bool IsDebugBuild { get; private set; } @@ -54,30 +51,46 @@ public class Program { StartupTimer.Start(); - Args.DebugMode = args.Contains("--debug"); - Args.DebugExceptionDialog = args.Contains("--debug-exception-dialog"); - Args.DebugSentry = args.Contains("--debug-sentry"); - Args.DebugOneClickInstall = args.Contains("--debug-one-click-install"); - Args.NoSentry = args.Contains("--no-sentry"); - Args.NoWindowChromeEffects = args.Contains("--no-window-chrome-effects"); - Args.ResetWindowPosition = args.Contains("--reset-window-position"); - Args.DisableGpuRendering = args.Contains("--disable-gpu"); - - // Launched for custom URI scheme, send to main process - if (args.Contains("--uri")) + SetDebugBuild(); + + var parseResult = Parser.Default + .ParseArguments(args) + .WithNotParsed(errors => + { + foreach (var error in errors) + { + Console.Error.WriteLine(error.ToString()); + } + }); + + Args = parseResult.Value; + + // Launched for custom URI scheme, handle and exit + if (Args.Uri is { } uriArg) { - var uriArg = args.ElementAtOrDefault(args.IndexOf("--uri") + 1); - if ( - Uri.TryCreate(uriArg, UriKind.Absolute, out var uri) - && string.Equals(uri.Scheme, UriHandler.Scheme, StringComparison.OrdinalIgnoreCase) - ) + try { - UriHandler.SendAndExit(uri); + if ( + Uri.TryCreate(uriArg, UriKind.Absolute, out var uri) + && string.Equals( + uri.Scheme, + UriHandler.Scheme, + StringComparison.OrdinalIgnoreCase + ) + ) + { + UriHandler.SendAndExit(uri); + } + + Environment.Exit(0); + } + catch (Exception e) + { + Console.Error.WriteLine($"Uri handler encountered an error: {e.Message}"); + Environment.Exit(1); } } - SetDebugBuild(); - HandleUpdateReplacement(); var infoVersion = Assembly diff --git a/StabilityMatrix.Avalonia/StabilityMatrix.Avalonia.csproj b/StabilityMatrix.Avalonia/StabilityMatrix.Avalonia.csproj index 555de930..6bcb2cb3 100644 --- a/StabilityMatrix.Avalonia/StabilityMatrix.Avalonia.csproj +++ b/StabilityMatrix.Avalonia/StabilityMatrix.Avalonia.csproj @@ -32,6 +32,7 @@ +