diff --git a/osu.Desktop/OsuGameDesktop.cs b/osu.Desktop/OsuGameDesktop.cs
index 21cea3ba76..efd3d358b7 100644
--- a/osu.Desktop/OsuGameDesktop.cs
+++ b/osu.Desktop/OsuGameDesktop.cs
@@ -17,6 +17,7 @@ using osu.Game.Updater;
using osu.Desktop.Windows;
using osu.Game.IO;
using osu.Game.IPC;
+using osu.Game.Online.Multiplayer;
using osu.Game.Utils;
using SDL2;
@@ -108,6 +109,25 @@ namespace osu.Desktop
}
}
+ public override bool RestartAppWhenExited()
+ {
+ switch (RuntimeInfo.OS)
+ {
+ case RuntimeInfo.Platform.Windows:
+ Debug.Assert(OperatingSystem.IsWindows());
+
+ // Of note, this is an async method in squirrel that adds an arbitrary delay before returning
+ // likely to ensure the external process is in a good state.
+ //
+ // We're not waiting on that here, but the outro playing before the actual exit should be enough
+ // to cover this.
+ Squirrel.UpdateManager.RestartAppWhenExited().FireAndForget();
+ return true;
+ }
+
+ return base.RestartAppWhenExited();
+ }
+
protected override void LoadComplete()
{
base.LoadComplete();
diff --git a/osu.Game.Tournament/Screens/Setup/TournamentSwitcher.cs b/osu.Game.Tournament/Screens/Setup/TournamentSwitcher.cs
index 7a8b03a7aa..01d86274d7 100644
--- a/osu.Game.Tournament/Screens/Setup/TournamentSwitcher.cs
+++ b/osu.Game.Tournament/Screens/Setup/TournamentSwitcher.cs
@@ -28,7 +28,11 @@ namespace osu.Game.Tournament.Screens.Setup
dropdown.Items = storage.ListTournaments();
dropdown.Current.BindValueChanged(v => Button.Enabled.Value = v.NewValue != startupTournament, true);
- Action = () => game.AttemptExit();
+ Action = () =>
+ {
+ game.RestartAppWhenExited();
+ game.AttemptExit();
+ };
folderButton.Action = () => storage.PresentExternally();
ButtonText = "Close osu!";
diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs
index 63efe0e2c8..6737caa5f9 100644
--- a/osu.Game/OsuGameBase.cs
+++ b/osu.Game/OsuGameBase.cs
@@ -515,6 +515,12 @@ namespace osu.Game
Scheduler.AddDelayed(AttemptExit, 2000);
}
+ ///
+ /// If supported by the platform, the game will automatically restart after the next exit.
+ ///
+ /// Whether a restart operation was queued.
+ public virtual bool RestartAppWhenExited() => false;
+
public bool Migrate(string path)
{
Logger.Log($@"Migrating osu! data from ""{Storage.GetFullPath(string.Empty)}"" to ""{path}""...");
diff --git a/osu.Game/Overlays/Settings/Sections/Graphics/RendererSettings.cs b/osu.Game/Overlays/Settings/Sections/Graphics/RendererSettings.cs
index a1f728ca87..d4cef3f4d1 100644
--- a/osu.Game/Overlays/Settings/Sections/Graphics/RendererSettings.cs
+++ b/osu.Game/Overlays/Settings/Sections/Graphics/RendererSettings.cs
@@ -67,10 +67,17 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics
if (r.NewValue == RendererType.Automatic && automaticRendererInUse)
return;
- dialogOverlay?.Push(new ConfirmDialog(GraphicsSettingsStrings.ChangeRendererConfirmation, () => game?.AttemptExit(), () =>
+ if (game?.RestartAppWhenExited() == true)
{
- renderer.Value = automaticRendererInUse ? RendererType.Automatic : host.ResolvedRenderer;
- }));
+ game.AttemptExit();
+ }
+ else
+ {
+ dialogOverlay?.Push(new ConfirmDialog(GraphicsSettingsStrings.ChangeRendererConfirmation, () => game?.AttemptExit(), () =>
+ {
+ renderer.Value = automaticRendererInUse ? RendererType.Automatic : host.ResolvedRenderer;
+ }));
+ }
});
// TODO: remove this once we support SDL+android.