diff --git a/src/UniGetUI.Avalonia/App.axaml.cs b/src/UniGetUI.Avalonia/App.axaml.cs index 57d23e90b..1bc752d99 100644 --- a/src/UniGetUI.Avalonia/App.axaml.cs +++ b/src/UniGetUI.Avalonia/App.axaml.cs @@ -8,6 +8,7 @@ using UniGetUI.Avalonia.Infrastructure; using UniGetUI.Avalonia.Views; using UniGetUI.Avalonia.Views.DialogPages; +using UniGetUI.Core.Data; using UniGetUI.PackageEngine; using CoreSettings = global::UniGetUI.Core.SettingsEngine.Settings; @@ -48,6 +49,13 @@ public override void OnFrameworkInitializationCompleted() ApplyTheme(CoreSettings.GetValue(CoreSettings.K.PreferredTheme)); var mainWindow = new MainWindow(); desktop.MainWindow = mainWindow; + + if (CoreData.WasDaemon) + { + // Start silently: hide the window as soon as Avalonia opens it. + mainWindow.Opened += (_, _) => mainWindow.Hide(); + } + _ = StartupAsync(mainWindow); } @@ -94,7 +102,13 @@ private static async Task StartupAsync(MainWindow mainWindow) // Yield once so the main window has time to open before // ShowDialog tries to attach to it as owner. await Task.Yield(); + + // ShowDialog requires a visible owner. In daemon mode the main window + // is hidden, so temporarily show it and re-hide after the dialog closes. + bool reshide = CoreData.WasDaemon; + if (reshide) mainWindow.Show(); await new CrashReportWindow(report).ShowDialog(mainWindow); + if (reshide) mainWindow.Hide(); } catch { /* must not prevent normal startup */ } } diff --git a/src/UniGetUI.Avalonia/Program.cs b/src/UniGetUI.Avalonia/Program.cs index ea2f59c1b..5d30ccb98 100644 --- a/src/UniGetUI.Avalonia/Program.cs +++ b/src/UniGetUI.Avalonia/Program.cs @@ -1,5 +1,6 @@ using System; using Avalonia; +using UniGetUI.Core.Data; namespace UniGetUI.Avalonia; @@ -14,6 +15,8 @@ public static void Main(string[] args) AppDomain.CurrentDomain.UnhandledException += (_, e) => CrashHandler.ReportFatalException((Exception)e.ExceptionObject); + CoreData.WasDaemon = CoreData.IsDaemon = args.Contains("--daemon"); + BuildAvaloniaApp().StartWithClassicDesktopLifetime(args); } diff --git a/src/UniGetUI/App.xaml.cs b/src/UniGetUI/App.xaml.cs index 6480addc0..4f42c199e 100644 --- a/src/UniGetUI/App.xaml.cs +++ b/src/UniGetUI/App.xaml.cs @@ -332,7 +332,8 @@ private async Task LoadComponentsAsync() // Create MainWindow InitializeMainWindow(); - MainWindow.Activate(); + if (!CoreData.WasDaemon) + MainWindow.Activate(); // Show crash report from the previous session on top of the loading // screen and wait for the user to dismiss it before continuing. @@ -340,6 +341,15 @@ private async Task LoadComponentsAsync() { try { + // In daemon mode the DWM/XAML threads are suspended; resume them + // temporarily so the crash window can render, then re-suspend. + bool resumedForCrash = CoreData.WasDaemon; + if (resumedForCrash) + { + DWMThreadHelper.ChangeState_DWM(false); + DWMThreadHelper.ChangeState_XAML(false); + } + string report = File.ReadAllText(CrashHandler.PendingCrashFile); File.Delete(CrashHandler.PendingCrashFile); var tcs = new TaskCompletionSource(); @@ -347,6 +357,12 @@ private async Task LoadComponentsAsync() crashWindow.Closed += (_, _) => tcs.TrySetResult(); crashWindow.Activate(); await tcs.Task; + + if (resumedForCrash) + { + DWMThreadHelper.ChangeState_DWM(true); + DWMThreadHelper.ChangeState_XAML(true); + } } catch { /* must not prevent normal startup */ } } @@ -505,7 +521,8 @@ private async Task CheckForMissingDependencies() protected override void OnLaunched(LaunchActivatedEventArgs args) { - MainWindow?.Activate(); + if (!CoreData.WasDaemon) + MainWindow?.Activate(); } public async Task ShowMainWindowFromRedirectAsync(AppActivationArguments rawArgs)