diff --git a/src/SharpEmu.GUI/App.axaml.cs b/src/SharpEmu.GUI/App.axaml.cs
index 111ef489..b88789cf 100644
--- a/src/SharpEmu.GUI/App.axaml.cs
+++ b/src/SharpEmu.GUI/App.axaml.cs
@@ -18,6 +18,7 @@ public partial class App : Application
{
if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
{
+ desktop.ShutdownMode = Avalonia.Controls.ShutdownMode.OnMainWindowClose;
desktop.MainWindow = new MainWindow();
}
diff --git a/src/SharpEmu.GUI/MainWindow.axaml b/src/SharpEmu.GUI/MainWindow.axaml
index 5c2a27e3..cd7fe0b5 100644
--- a/src/SharpEmu.GUI/MainWindow.axaml
+++ b/src/SharpEmu.GUI/MainWindow.axaml
@@ -73,19 +73,23 @@ SPDX-License-Identifier: GPL-2.0-or-later
Classes="windowChrome"
ToolTip.Tip="Minimize"
AutomationProperties.Name="Minimize window">
-
+
diff --git a/src/SharpEmu.GUI/MainWindow.axaml.cs b/src/SharpEmu.GUI/MainWindow.axaml.cs
index 11607290..ac085d11 100644
--- a/src/SharpEmu.GUI/MainWindow.axaml.cs
+++ b/src/SharpEmu.GUI/MainWindow.axaml.cs
@@ -165,8 +165,6 @@ public partial class MainWindow : Window
ConsoleList.ItemsSource = _consoleLines;
_consoleMirror = GuiConsoleMirror.Install((line, isError) =>
_pendingLines.Enqueue((line, isError)));
- Closed += (_, _) => _emulator?.Stop();
-
_consoleFlushTimer = new DispatcherTimer
{
Interval = TimeSpan.FromMilliseconds(80),
@@ -297,7 +295,8 @@ public partial class MainWindow : Window
CtxRemove.Click += (_, _) => RemoveSelectedFromLibrary();
Opened += async (_, _) => await OnOpenedAsync();
- Closing += (_, _) => OnWindowClosing();
+ Closing += (_, _) => BeginWindowClosing();
+ Closed += (_, _) => CompleteWindowClosing();
SdlLauncherGamepad.EnsureStarted();
_gamepadTimer = new DispatcherTimer
@@ -816,22 +815,44 @@ public partial class MainWindow : Window
// still needs a preview hook for its own shortcuts.
}
- private void OnWindowClosing()
+ private void BeginWindowClosing()
{
+ if (_isClosing)
+ {
+ return;
+ }
+
_isClosing = true;
Interlocked.Increment(ref _libraryScanGeneration);
Interlocked.Increment(ref _detailLoadGeneration);
- _libraryWatcher.Dispose();
- _settings.Save();
_consoleFlushTimer.Stop();
_gamepadTimer.Stop();
- SdlLauncherGamepad.Shutdown();
- _sndPreview.Stop();
- _discord?.Dispose();
- _consoleWindow?.Close();
- _emulator?.Dispose();
- _consoleMirror?.Dispose();
- DropFileLog();
+ }
+
+ private void CompleteWindowClosing()
+ {
+ RunShutdownStep("library watcher", _libraryWatcher.Dispose);
+ RunShutdownStep("settings", _settings.Save);
+ RunShutdownStep("SDL gamepad", SdlLauncherGamepad.Shutdown);
+ RunShutdownStep("title music", _sndPreview.Stop);
+ RunShutdownStep("Discord Rich Presence", () => _discord?.Dispose());
+ RunShutdownStep("console window", () => _consoleWindow?.Close());
+ RunShutdownStep("emulator process", () => _emulator?.Dispose());
+ RunShutdownStep("console mirror", () => _consoleMirror?.Dispose());
+ RunShutdownStep("file log", DropFileLog);
+ }
+
+ private static void RunShutdownStep(string component, Action action)
+ {
+ try
+ {
+ action();
+ }
+ catch (Exception exception)
+ {
+ Console.Error.WriteLine(
+ $"[GUI][WARN] Failed to clean up {component}: {exception}");
+ }
}
private void OnTitleBarPointerPressed(object? sender, PointerPressedEventArgs e)
@@ -868,14 +889,17 @@ public partial class MainWindow : Window
return;
}
- var isMaximized = WindowState == WindowState.Maximized;
- glyph.Text = isMaximized ? "❐" : "□";
- ToolTip.SetTip(button, isMaximized ? "Restore" : "Maximize");
- AutomationProperties.SetName(
- button,
- isMaximized ? "Restore window" : "Maximize window");
+ var state = GetMaximizeButtonState(WindowState);
+ glyph.Text = state.Glyph;
+ ToolTip.SetTip(button, state.ToolTip);
+ AutomationProperties.SetName(button, state.AutomationName);
}
+ internal static WindowMaximizeButtonState GetMaximizeButtonState(WindowState windowState) =>
+ windowState == WindowState.Maximized
+ ? new("filter_none", "Restore", "Restore window")
+ : new("crop_square", "Maximize", "Maximize window");
+
private void UpdateWindowChromeState()
{
UpdateMaximizeButton();
diff --git a/src/SharpEmu.GUI/Themes/Styles/Chrome.axaml b/src/SharpEmu.GUI/Themes/Styles/Chrome.axaml
index 6ac47f3f..8d3959e9 100644
--- a/src/SharpEmu.GUI/Themes/Styles/Chrome.axaml
+++ b/src/SharpEmu.GUI/Themes/Styles/Chrome.axaml
@@ -19,6 +19,12 @@ Window chrome button sizing and interaction states.
+
+