From eebc39228db4663e03fa73306e725424f7ce1273 Mon Sep 17 00:00:00 2001
From: TSRBerry <20988865+TSRBerry@users.noreply.github.com>
Date: Sun, 13 Nov 2022 00:36:36 +0100
Subject: [PATCH] UI: Allow overriding graphics backend + Move command line
 parser into a new class (#3707)

* Ava: Keep command line args when restarting

* UI: Move common UI functions to ProgramHelper

Add command line option to override the configured graphics backend

* Ava: Add CleanupUpdate task back

* Remove unused usings

* Revert combining common UI functions

Rename ProgramHelper to CommandLineState
Move command line parsing to CommandLineState

* Rename CommandLineProfile to Profile

* Fix assigning the wrong array to Arguments
---
 Ryujinx.Ava/App.axaml.cs                      |  4 +-
 Ryujinx.Ava/Program.cs                        | 65 +++++----------
 .../Ui/ViewModels/SettingsViewModel.cs        |  4 -
 Ryujinx.Ava/Ui/Windows/MainWindow.axaml.cs    |  5 +-
 Ryujinx.Ui.Common/Helper/CommandLineState.cs  | 81 +++++++++++++++++++
 Ryujinx/Modules/Updater/UpdateDialog.cs       |  7 +-
 Ryujinx/Program.cs                            | 78 ++++++------------
 Ryujinx/Ui/MainWindow.cs                      |  4 +-
 8 files changed, 137 insertions(+), 111 deletions(-)
 create mode 100644 Ryujinx.Ui.Common/Helper/CommandLineState.cs

diff --git a/Ryujinx.Ava/App.axaml.cs b/Ryujinx.Ava/App.axaml.cs
index 180c74b438..3ab1b7b8fb 100644
--- a/Ryujinx.Ava/App.axaml.cs
+++ b/Ryujinx.Ava/App.axaml.cs
@@ -10,6 +10,7 @@ using Ryujinx.Ava.Ui.Windows;
 using Ryujinx.Common;
 using Ryujinx.Common.Logging;
 using Ryujinx.Ui.Common.Configuration;
+using Ryujinx.Ui.Common.Helper;
 using System;
 using System.Diagnostics;
 using System.IO;
@@ -64,8 +65,7 @@ namespace Ryujinx.Ava
                     if (result == UserResult.Yes)
                     {
                         var path = Process.GetCurrentProcess().MainModule.FileName;
-                        var info = new ProcessStartInfo() { FileName = path, UseShellExecute = false };
-                        var proc = Process.Start(info);
+                        var proc = Process.Start(path, CommandLineState.Arguments);
                         desktop.Shutdown();
                         Environment.Exit(0);
                     }
diff --git a/Ryujinx.Ava/Program.cs b/Ryujinx.Ava/Program.cs
index 61b184c61e..053bccab65 100644
--- a/Ryujinx.Ava/Program.cs
+++ b/Ryujinx.Ava/Program.cs
@@ -13,6 +13,7 @@ using Ryujinx.Common.SystemInfo;
 using Ryujinx.Modules;
 using Ryujinx.Ui.Common;
 using Ryujinx.Ui.Common.Configuration;
+using Ryujinx.Ui.Common.Helper;
 using System;
 using System.IO;
 using System.Runtime.InteropServices;
@@ -26,7 +27,6 @@ namespace Ryujinx.Ava
         public static double ActualScaleFactor { get; set; }
         public static string Version { get; private set; }
         public static string ConfigurationPath { get; private set; }
-        public static string CommandLineProfile { get; set; }
         public static bool PreviewerDetached { get; private set; }
 
         public static RenderTimer RenderTimer { get; private set; }
@@ -87,46 +87,8 @@ namespace Ryujinx.Ava
 
         private static void Initialize(string[] args)
         {
-            // Parse Arguments.
-            string launchPathArg = null;
-            string baseDirPathArg = null;
-            bool startFullscreenArg = false;
-
-            for (int i = 0; i < args.Length; ++i)
-            {
-                string arg = args[i];
-
-                if (arg == "-r" || arg == "--root-data-dir")
-                {
-                    if (i + 1 >= args.Length)
-                    {
-                        Logger.Error?.Print(LogClass.Application, $"Invalid option '{arg}'");
-
-                        continue;
-                    }
-
-                    baseDirPathArg = args[++i];
-                }
-                else if (arg == "-p" || arg == "--profile")
-                {
-                    if (i + 1 >= args.Length)
-                    {
-                        Logger.Error?.Print(LogClass.Application, $"Invalid option '{arg}'");
-
-                        continue;
-                    }
-
-                    CommandLineProfile = args[++i];
-                }
-                else if (arg == "-f" || arg == "--fullscreen")
-                {
-                    startFullscreenArg = true;
-                }
-                else
-                {
-                    launchPathArg = arg;
-                }
-            }
+            // Parse arguments
+            CommandLineState.ParseArguments(args);
 
             // Delete backup files after updating.
             Task.Run(Updater.CleanupUpdate);
@@ -135,10 +97,10 @@ namespace Ryujinx.Ava
 
             // Hook unhandled exception and process exit events.
             AppDomain.CurrentDomain.UnhandledException += (object sender, UnhandledExceptionEventArgs e) => ProcessUnhandledException(e.ExceptionObject as Exception, e.IsTerminating);
-            AppDomain.CurrentDomain.ProcessExit += (object sender, EventArgs e) => Exit();
+            AppDomain.CurrentDomain.ProcessExit        += (object sender, EventArgs e)                   => Exit();
 
             // Setup base data directory.
-            AppDataManager.Initialize(baseDirPathArg);
+            AppDataManager.Initialize(CommandLineState.BaseDirPathArg);
 
             // Initialize the configuration.
             ConfigurationState.Initialize();
@@ -173,9 +135,9 @@ namespace Ryujinx.Ava
                 }
             }
 
-            if (launchPathArg != null)
+            if (CommandLineState.LaunchPathArg != null)
             {
-                MainWindow.DeferLoadApplication(launchPathArg, startFullscreenArg);
+                MainWindow.DeferLoadApplication(CommandLineState.LaunchPathArg, CommandLineState.StartFullscreenArg);
             }
         }
 
@@ -215,6 +177,19 @@ namespace Ryujinx.Ava
                     Logger.Warning?.PrintMsg(LogClass.Application, $"Failed to load config! Loading the default config instead.\nFailed config location {ConfigurationPath}");
                 }
             }
+
+            // Check if graphics backend was overridden
+            if (CommandLineState.OverrideGraphicsBackend != null)
+            {
+                if (CommandLineState.OverrideGraphicsBackend.ToLower() == "opengl")
+                {
+                    ConfigurationState.Instance.Graphics.GraphicsBackend.Value = GraphicsBackend.OpenGl;
+                }
+                else if (CommandLineState.OverrideGraphicsBackend.ToLower() == "vulkan")
+                {
+                    ConfigurationState.Instance.Graphics.GraphicsBackend.Value = GraphicsBackend.Vulkan;
+                }
+            }
         }
 
         private static void PrintSystemInfo()
diff --git a/Ryujinx.Ava/Ui/ViewModels/SettingsViewModel.cs b/Ryujinx.Ava/Ui/ViewModels/SettingsViewModel.cs
index f4807023dd..088bf3ac9e 100644
--- a/Ryujinx.Ava/Ui/ViewModels/SettingsViewModel.cs
+++ b/Ryujinx.Ava/Ui/ViewModels/SettingsViewModel.cs
@@ -21,14 +21,10 @@ using Ryujinx.HLE.HOS.Services.Time.TimeZone;
 using Ryujinx.Input;
 using Ryujinx.Ui.Common.Configuration;
 using Ryujinx.Ui.Common.Configuration.System;
-using Silk.NET.Vulkan;
 using System;
 using System.Collections.Generic;
 using System.Collections.ObjectModel;
-using System.Diagnostics;
 using System.Linq;
-using System.Runtime.InteropServices;
-using System.Threading.Tasks;
 using TimeZone = Ryujinx.Ava.Ui.Models.TimeZone;
 
 namespace Ryujinx.Ava.Ui.ViewModels
diff --git a/Ryujinx.Ava/Ui/Windows/MainWindow.axaml.cs b/Ryujinx.Ava/Ui/Windows/MainWindow.axaml.cs
index ae79b1c9d6..774178d662 100644
--- a/Ryujinx.Ava/Ui/Windows/MainWindow.axaml.cs
+++ b/Ryujinx.Ava/Ui/Windows/MainWindow.axaml.cs
@@ -23,6 +23,7 @@ using Ryujinx.Modules;
 using Ryujinx.Ui.App.Common;
 using Ryujinx.Ui.Common;
 using Ryujinx.Ui.Common.Configuration;
+using Ryujinx.Ui.Common.Helper;
 using SixLabors.ImageSharp.PixelFormats;
 using System;
 using System.ComponentModel;
@@ -247,7 +248,7 @@ namespace Ryujinx.Ava.Ui.Windows
             {
                 RendererControl.CreateVulkan();
             }
-            
+
             AppHost = new AppHost(RendererControl, InputManager, path, VirtualFileSystem, ContentManager, AccountManager, _userChannelPersistence, this);
 
             if (!AppHost.LoadGuestApplication().Result)
@@ -432,7 +433,7 @@ namespace Ryujinx.Ava.Ui.Windows
             // Consider removing this at some point in the future when we don't need to worry about old saves.
             VirtualFileSystem.FixExtraData(LibHacHorizonManager.RyujinxClient);
 
-            AccountManager = new AccountManager(LibHacHorizonManager.RyujinxClient, Program.CommandLineProfile);
+            AccountManager = new AccountManager(LibHacHorizonManager.RyujinxClient, CommandLineState.Profile);
 
             VirtualFileSystem.ReloadKeySet();
 
diff --git a/Ryujinx.Ui.Common/Helper/CommandLineState.cs b/Ryujinx.Ui.Common/Helper/CommandLineState.cs
new file mode 100644
index 0000000000..cda4af4ed8
--- /dev/null
+++ b/Ryujinx.Ui.Common/Helper/CommandLineState.cs
@@ -0,0 +1,81 @@
+using Ryujinx.Common.Logging;
+using System.Collections.Generic;
+
+namespace Ryujinx.Ui.Common.Helper
+{
+    public static class CommandLineState
+    {
+        public static string[] Arguments { get; private set; }
+
+        public static string OverrideGraphicsBackend { get; private set; }
+        public static string BaseDirPathArg          { get; private set; }
+        public static string Profile                 { get; private set; }
+        public static string LaunchPathArg           { get; private set; }
+        public static bool   StartFullscreenArg      { get; private set; }
+
+        public static void ParseArguments(string[] args)
+        {
+            List<string> arguments = new();
+
+            // Parse Arguments.
+            for (int i = 0; i < args.Length; ++i)
+            {
+                string arg = args[i];
+
+                switch (arg)
+                {
+                    case "-r":
+                    case "--root-data-dir":
+                        if (i + 1 >= args.Length)
+                        {
+                            Logger.Error?.Print(LogClass.Application, $"Invalid option '{arg}'");
+
+                            continue;
+                        }
+
+                        BaseDirPathArg = args[++i];
+
+                        arguments.Add(arg);
+                        arguments.Add(args[i]);
+                        break;
+                    case "-p":
+                    case "--profile":
+                        if (i + 1 >= args.Length)
+                        {
+                            Logger.Error?.Print(LogClass.Application, $"Invalid option '{arg}'");
+
+                            continue;
+                        }
+
+                        Profile = args[++i];
+
+                        arguments.Add(arg);
+                        arguments.Add(args[i]);
+                        break;
+                    case "-f":
+                    case "--fullscreen":
+                        StartFullscreenArg = true;
+
+                        arguments.Add(arg);
+                        break;
+                    case "-g":
+                    case "--graphics-backend":
+                        if (i + 1 >= args.Length)
+                        {
+                            Logger.Error?.Print(LogClass.Application, $"Invalid option '{arg}'");
+
+                            continue;
+                        }
+
+                        OverrideGraphicsBackend = args[++i];
+                        break;
+                    default:
+                        LaunchPathArg = arg;
+                        break;
+                }
+            }
+
+            Arguments = arguments.ToArray();
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/Modules/Updater/UpdateDialog.cs b/Ryujinx/Modules/Updater/UpdateDialog.cs
index cdf85427eb..cb71fafc9b 100644
--- a/Ryujinx/Modules/Updater/UpdateDialog.cs
+++ b/Ryujinx/Modules/Updater/UpdateDialog.cs
@@ -2,9 +2,9 @@ using Gdk;
 using Gtk;
 using Ryujinx.Ui;
 using Ryujinx.Ui.Common.Configuration;
+using Ryujinx.Ui.Common.Helper;
 using System;
 using System.Diagnostics;
-using System.Linq;
 using System.Reflection;
 
 namespace Ryujinx.Modules
@@ -48,9 +48,8 @@ namespace Ryujinx.Modules
             {
                 string ryuName = OperatingSystem.IsWindows() ? "Ryujinx.exe" : "Ryujinx";
                 string ryuExe  = System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, ryuName);
-                var ryuArg  = Environment.GetCommandLineArgs().AsEnumerable().Skip(1);
 
-                Process.Start(ryuExe, ryuArg);
+                Process.Start(ryuExe, CommandLineState.Arguments);
 
                 Environment.Exit(0);
             }
@@ -81,4 +80,4 @@ namespace Ryujinx.Modules
             Dispose();
         }
     }
-}
+}
\ No newline at end of file
diff --git a/Ryujinx/Program.cs b/Ryujinx/Program.cs
index d9db941de6..a91f9aa521 100644
--- a/Ryujinx/Program.cs
+++ b/Ryujinx/Program.cs
@@ -6,10 +6,11 @@ using Ryujinx.Common.GraphicsDriver;
 using Ryujinx.Common.Logging;
 using Ryujinx.Common.System;
 using Ryujinx.Common.SystemInfo;
-using Ryujinx.Ui.Common.Configuration;
 using Ryujinx.Modules;
 using Ryujinx.Ui;
 using Ryujinx.Ui.Common;
+using Ryujinx.Ui.Common.Configuration;
+using Ryujinx.Ui.Common.Helper;
 using Ryujinx.Ui.Widgets;
 using SixLabors.ImageSharp.Formats.Jpeg;
 using System;
@@ -28,8 +29,6 @@ namespace Ryujinx
 
         public static string ConfigurationPath { get; set; }
 
-        public static string CommandLineProfile { get; set; }
-
         [DllImport("libX11")]
         private extern static int XInitThreads();
 
@@ -47,46 +46,13 @@ namespace Ryujinx
                 MessageBoxA(IntPtr.Zero, "You are running an outdated version of Windows.\n\nStarting on June 1st 2022, Ryujinx will only support Windows 10 1803 and newer.\n", $"Ryujinx {Version}", MB_ICONWARNING);
             }
 
-            // Parse Arguments.
-            string launchPathArg      = null;
-            string baseDirPathArg     = null;
-            bool   startFullscreenArg = false;
+            // Parse arguments
+            CommandLineState.ParseArguments(args);
 
-            for (int i = 0; i < args.Length; ++i)
-            {
-                string arg = args[i];
-
-                if (arg == "-r" || arg == "--root-data-dir")
-                {
-                    if (i + 1 >= args.Length)
-                    {
-                        Logger.Error?.Print(LogClass.Application, $"Invalid option '{arg}'");
-
-                        continue;
-                    }
-
-                    baseDirPathArg = args[++i];
-                }
-                else if (arg == "-p" || arg == "--profile")
-                {
-                    if (i + 1 >= args.Length)
-                    {
-                        Logger.Error?.Print(LogClass.Application, $"Invalid option '{arg}'");
-
-                        continue;
-                    }
-
-                    CommandLineProfile = args[++i];
-                }
-                else if (arg == "-f" || arg == "--fullscreen")
-                {
-                    startFullscreenArg = true;
-                }
-                else if (launchPathArg == null)
-                {
-                    launchPathArg = arg;
-                }
-            }
+            // Hook unhandled exception and process exit events.
+            GLib.ExceptionManager.UnhandledException   += (GLib.UnhandledExceptionArgs e)                => ProcessUnhandledException(e.ExceptionObject as Exception, e.IsTerminating);
+            AppDomain.CurrentDomain.UnhandledException += (object sender, UnhandledExceptionEventArgs e) => ProcessUnhandledException(e.ExceptionObject as Exception, e.IsTerminating);
+            AppDomain.CurrentDomain.ProcessExit        += (object sender, EventArgs e)                   => Exit();
 
             // Make process DPI aware for proper window sizing on high-res screens.
             ForceDpiAware.Windows();
@@ -95,8 +61,6 @@ namespace Ryujinx
             // Delete backup files after updating.
             Task.Run(Updater.CleanupUpdate);
 
-            Console.Title = $"Ryujinx Console {Version}";
-
             // NOTE: GTK3 doesn't init X11 in a multi threaded way.
             // This ends up causing race condition and abort of XCB when a context is created by SPB (even if SPB do call XInitThreads).
             if (OperatingSystem.IsLinux())
@@ -107,13 +71,8 @@ namespace Ryujinx
             string systemPath = Environment.GetEnvironmentVariable("Path", EnvironmentVariableTarget.Machine);
             Environment.SetEnvironmentVariable("Path", $"{Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "bin")};{systemPath}");
 
-            // Hook unhandled exception and process exit events.
-            GLib.ExceptionManager.UnhandledException   += (GLib.UnhandledExceptionArgs e)                => ProcessUnhandledException(e.ExceptionObject as Exception, e.IsTerminating);
-            AppDomain.CurrentDomain.UnhandledException += (object sender, UnhandledExceptionEventArgs e) => ProcessUnhandledException(e.ExceptionObject as Exception, e.IsTerminating);
-            AppDomain.CurrentDomain.ProcessExit        += (object sender, EventArgs e)                   => Exit();
-
             // Setup base data directory.
-            AppDataManager.Initialize(baseDirPathArg);
+            AppDataManager.Initialize(CommandLineState.BaseDirPathArg);
 
             // Initialize the configuration.
             ConfigurationState.Initialize();
@@ -173,6 +132,21 @@ namespace Ryujinx
                 }
             }
 
+            // Check if graphics backend was overridden
+            if (CommandLineState.OverrideGraphicsBackend != null)
+            {
+                if (CommandLineState.OverrideGraphicsBackend.ToLower() == "opengl")
+                {
+                    ConfigurationState.Instance.Graphics.GraphicsBackend.Value = GraphicsBackend.OpenGl;
+                    showVulkanPrompt = false;
+                }
+                else if (CommandLineState.OverrideGraphicsBackend.ToLower() == "vulkan")
+                {
+                    ConfigurationState.Instance.Graphics.GraphicsBackend.Value = GraphicsBackend.Vulkan;
+                    showVulkanPrompt = false;
+                }
+            }
+
             // Logging system information.
             PrintSystemInfo();
 
@@ -195,9 +169,9 @@ namespace Ryujinx
             MainWindow mainWindow = new MainWindow();
             mainWindow.Show();
 
-            if (launchPathArg != null)
+            if (CommandLineState.LaunchPathArg != null)
             {
-                mainWindow.LoadApplication(launchPathArg, startFullscreenArg);
+                mainWindow.LoadApplication(CommandLineState.LaunchPathArg, CommandLineState.StartFullscreenArg);
             }
 
             if (ConfigurationState.Instance.CheckUpdatesOnStart.Value && Updater.CanUpdate(false))
diff --git a/Ryujinx/Ui/MainWindow.cs b/Ryujinx/Ui/MainWindow.cs
index c0b2e1b66d..c78b7a2f23 100644
--- a/Ryujinx/Ui/MainWindow.cs
+++ b/Ryujinx/Ui/MainWindow.cs
@@ -179,7 +179,7 @@ namespace Ryujinx.Ui
             VirtualFileSystem.FixExtraData(_libHacHorizonManager.RyujinxClient);
 
             _contentManager         = new ContentManager(_virtualFileSystem);
-            _accountManager         = new AccountManager(_libHacHorizonManager.RyujinxClient, Program.CommandLineProfile);
+            _accountManager         = new AccountManager(_libHacHorizonManager.RyujinxClient, CommandLineState.Profile);
             _userChannelPersistence = new UserChannelPersistence();
 
             // Instantiate GUI objects.
@@ -1752,4 +1752,4 @@ namespace Ryujinx.Ui
             UpdateGameTable();
         }
     }
-}
+}
\ No newline at end of file