Program.cs
1 using Avalonia; 2 using Avalonia.Threading; 3 using Ryujinx.Ava.UI.Helpers; 4 using Ryujinx.Ava.UI.Windows; 5 using Ryujinx.Common; 6 using Ryujinx.Common.Configuration; 7 using Ryujinx.Common.GraphicsDriver; 8 using Ryujinx.Common.Logging; 9 using Ryujinx.Common.SystemInterop; 10 using Ryujinx.Graphics.Vulkan.MoltenVK; 11 using Ryujinx.Modules; 12 using Ryujinx.SDL2.Common; 13 using Ryujinx.UI.Common; 14 using Ryujinx.UI.Common.Configuration; 15 using Ryujinx.UI.Common.Helper; 16 using Ryujinx.UI.Common.SystemInfo; 17 using System; 18 using System.IO; 19 using System.Runtime.InteropServices; 20 using System.Threading.Tasks; 21 22 namespace Ryujinx.Ava 23 { 24 internal partial class Program 25 { 26 public static double WindowScaleFactor { get; set; } 27 public static double DesktopScaleFactor { get; set; } = 1.0; 28 public static string Version { get; private set; } 29 public static string ConfigurationPath { get; private set; } 30 public static bool PreviewerDetached { get; private set; } 31 public static bool UseHardwareAcceleration { get; private set; } 32 33 [LibraryImport("user32.dll", SetLastError = true)] 34 public static partial int MessageBoxA(IntPtr hWnd, [MarshalAs(UnmanagedType.LPStr)] string text, [MarshalAs(UnmanagedType.LPStr)] string caption, uint type); 35 36 private const uint MbIconwarning = 0x30; 37 38 public static void Main(string[] args) 39 { 40 Version = ReleaseInformation.Version; 41 42 if (OperatingSystem.IsWindows() && !OperatingSystem.IsWindowsVersionAtLeast(10, 0, 17134)) 43 { 44 _ = MessageBoxA(IntPtr.Zero, "You are running an outdated version of Windows.\n\nRyujinx supports Windows 10 version 1803 and newer.\n", $"Ryujinx {Version}", MbIconwarning); 45 } 46 47 PreviewerDetached = true; 48 49 Initialize(args); 50 51 LoggerAdapter.Register(); 52 53 BuildAvaloniaApp().StartWithClassicDesktopLifetime(args); 54 } 55 56 public static AppBuilder BuildAvaloniaApp() 57 { 58 return AppBuilder.Configure<App>() 59 .UsePlatformDetect() 60 .With(new X11PlatformOptions 61 { 62 EnableMultiTouch = true, 63 EnableIme = true, 64 EnableInputFocusProxy = Environment.GetEnvironmentVariable("XDG_CURRENT_DESKTOP") == "gamescope", 65 RenderingMode = UseHardwareAcceleration ? 66 new[] { X11RenderingMode.Glx, X11RenderingMode.Software } : 67 new[] { X11RenderingMode.Software }, 68 }) 69 .With(new Win32PlatformOptions 70 { 71 WinUICompositionBackdropCornerRadius = 8.0f, 72 RenderingMode = UseHardwareAcceleration ? 73 new[] { Win32RenderingMode.AngleEgl, Win32RenderingMode.Software } : 74 new[] { Win32RenderingMode.Software }, 75 }) 76 .UseSkia(); 77 } 78 79 private static void Initialize(string[] args) 80 { 81 // Parse arguments 82 CommandLineState.ParseArguments(args); 83 84 if (OperatingSystem.IsMacOS()) 85 { 86 MVKInitialization.InitializeResolver(); 87 } 88 89 // Delete backup files after updating. 90 Task.Run(Updater.CleanupUpdate); 91 92 Console.Title = $"Ryujinx Console {Version}"; 93 94 // Hook unhandled exception and process exit events. 95 AppDomain.CurrentDomain.UnhandledException += (sender, e) => ProcessUnhandledException(e.ExceptionObject as Exception, e.IsTerminating); 96 AppDomain.CurrentDomain.ProcessExit += (sender, e) => Exit(); 97 98 // Setup base data directory. 99 AppDataManager.Initialize(CommandLineState.BaseDirPathArg); 100 101 // Initialize the configuration. 102 ConfigurationState.Initialize(); 103 104 // Initialize the logger system. 105 LoggerModule.Initialize(); 106 107 // Initialize Discord integration. 108 DiscordIntegrationModule.Initialize(); 109 110 // Initialize SDL2 driver 111 SDL2Driver.MainThreadDispatcher = action => Dispatcher.UIThread.InvokeAsync(action, DispatcherPriority.Input); 112 113 ReloadConfig(); 114 115 WindowScaleFactor = ForceDpiAware.GetWindowScaleFactor(); 116 117 // Logging system information. 118 PrintSystemInfo(); 119 120 // Enable OGL multithreading on the driver, and some other flags. 121 DriverUtilities.InitDriverConfig(ConfigurationState.Instance.Graphics.BackendThreading == BackendThreading.Off); 122 123 // Check if keys exists. 124 if (!File.Exists(Path.Combine(AppDataManager.KeysDirPath, "prod.keys"))) 125 { 126 if (!(AppDataManager.Mode == AppDataManager.LaunchMode.UserProfile && File.Exists(Path.Combine(AppDataManager.KeysDirPathUser, "prod.keys")))) 127 { 128 MainWindow.ShowKeyErrorOnLoad = true; 129 } 130 } 131 132 if (CommandLineState.LaunchPathArg != null) 133 { 134 MainWindow.DeferLoadApplication(CommandLineState.LaunchPathArg, CommandLineState.LaunchApplicationId, CommandLineState.StartFullscreenArg); 135 } 136 } 137 138 public static void ReloadConfig() 139 { 140 string localConfigurationPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, ReleaseInformation.ConfigName); 141 string appDataConfigurationPath = Path.Combine(AppDataManager.BaseDirPath, ReleaseInformation.ConfigName); 142 143 // Now load the configuration as the other subsystems are now registered 144 if (File.Exists(localConfigurationPath)) 145 { 146 ConfigurationPath = localConfigurationPath; 147 } 148 else if (File.Exists(appDataConfigurationPath)) 149 { 150 ConfigurationPath = appDataConfigurationPath; 151 } 152 153 if (ConfigurationPath == null) 154 { 155 // No configuration, we load the default values and save it to disk 156 ConfigurationPath = appDataConfigurationPath; 157 Logger.Notice.Print(LogClass.Application, $"No configuration file found. Saving default configuration to: {ConfigurationPath}"); 158 159 ConfigurationState.Instance.LoadDefault(); 160 ConfigurationState.Instance.ToFileFormat().SaveConfig(ConfigurationPath); 161 } 162 else 163 { 164 Logger.Notice.Print(LogClass.Application, $"Loading configuration from: {ConfigurationPath}"); 165 166 if (ConfigurationFileFormat.TryLoad(ConfigurationPath, out ConfigurationFileFormat configurationFileFormat)) 167 { 168 ConfigurationState.Instance.Load(configurationFileFormat, ConfigurationPath); 169 } 170 else 171 { 172 Logger.Warning?.PrintMsg(LogClass.Application, $"Failed to load config! Loading the default config instead.\nFailed config location: {ConfigurationPath}"); 173 174 ConfigurationState.Instance.LoadDefault(); 175 } 176 } 177 178 UseHardwareAcceleration = ConfigurationState.Instance.EnableHardwareAcceleration.Value; 179 180 // Check if graphics backend was overridden 181 if (CommandLineState.OverrideGraphicsBackend != null) 182 { 183 if (CommandLineState.OverrideGraphicsBackend.ToLower() == "opengl") 184 { 185 ConfigurationState.Instance.Graphics.GraphicsBackend.Value = GraphicsBackend.OpenGl; 186 } 187 else if (CommandLineState.OverrideGraphicsBackend.ToLower() == "vulkan") 188 { 189 ConfigurationState.Instance.Graphics.GraphicsBackend.Value = GraphicsBackend.Vulkan; 190 } 191 } 192 193 // Check if docked mode was overriden. 194 if (CommandLineState.OverrideDockedMode.HasValue) 195 { 196 ConfigurationState.Instance.System.EnableDockedMode.Value = CommandLineState.OverrideDockedMode.Value; 197 } 198 199 // Check if HideCursor was overridden. 200 if (CommandLineState.OverrideHideCursor is not null) 201 { 202 ConfigurationState.Instance.HideCursor.Value = CommandLineState.OverrideHideCursor!.ToLower() switch 203 { 204 "never" => HideCursorMode.Never, 205 "onidle" => HideCursorMode.OnIdle, 206 "always" => HideCursorMode.Always, 207 _ => ConfigurationState.Instance.HideCursor.Value, 208 }; 209 } 210 211 // Check if hardware-acceleration was overridden. 212 if (CommandLineState.OverrideHardwareAcceleration != null) 213 { 214 UseHardwareAcceleration = CommandLineState.OverrideHardwareAcceleration.Value; 215 } 216 } 217 218 private static void PrintSystemInfo() 219 { 220 Logger.Notice.Print(LogClass.Application, $"Ryujinx Version: {Version}"); 221 SystemInfo.Gather().Print(); 222 223 Logger.Notice.Print(LogClass.Application, $"Logs Enabled: {(Logger.GetEnabledLevels().Count == 0 ? "<None>" : string.Join(", ", Logger.GetEnabledLevels()))}"); 224 225 if (AppDataManager.Mode == AppDataManager.LaunchMode.Custom) 226 { 227 Logger.Notice.Print(LogClass.Application, $"Launch Mode: Custom Path {AppDataManager.BaseDirPath}"); 228 } 229 else 230 { 231 Logger.Notice.Print(LogClass.Application, $"Launch Mode: {AppDataManager.Mode}"); 232 } 233 } 234 235 private static void ProcessUnhandledException(Exception ex, bool isTerminating) 236 { 237 string message = $"Unhandled exception caught: {ex}"; 238 239 Logger.Error?.PrintMsg(LogClass.Application, message); 240 241 if (Logger.Error == null) 242 { 243 Logger.Notice.PrintMsg(LogClass.Application, message); 244 } 245 246 if (isTerminating) 247 { 248 Exit(); 249 } 250 } 251 252 public static void Exit() 253 { 254 DiscordIntegrationModule.Exit(); 255 256 Logger.Shutdown(); 257 } 258 } 259 }