From f1d45ccc53890ddb520996a663e6a5362919dc9b Mon Sep 17 00:00:00 2001 From: CloneWith Date: Thu, 11 Dec 2025 14:04:39 +0800 Subject: [PATCH 1/3] Use g0v0-specific prefix for association registration --- osu.Desktop/Windows/WindowsAssociationManager.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Desktop/Windows/WindowsAssociationManager.cs b/osu.Desktop/Windows/WindowsAssociationManager.cs index 4a5fc6218e00..2b2380858051 100644 --- a/osu.Desktop/Windows/WindowsAssociationManager.cs +++ b/osu.Desktop/Windows/WindowsAssociationManager.cs @@ -37,11 +37,11 @@ public static class WindowsAssociationManager /// Program ID prefix used for file associations. Should be relatively short since the full program ID has a 39 character limit, /// see https://learn.microsoft.com/en-us/windows/win32/com/-progid--key. /// - private const string program_id_file_prefix = "osu.File"; + private const string program_id_file_prefix = "g0v0.osu.File"; - private const string program_id_protocol_prefix = "osu.Uri"; + private const string program_id_protocol_prefix = "g0v0.osu.Uri"; - private static readonly ApplicationCapability application_capability = new ApplicationCapability(@"osu", @"Software\ppy\osu\Capabilities", "osu!(lazer)"); + private static readonly ApplicationCapability application_capability = new ApplicationCapability(@"osu-gu", @"Software\g0v0\osu\Capabilities", "osu!(lazer) (g0v0)"); private static readonly FileAssociation[] file_associations = { From d82b85c3cdb62ff79ad636266963a41645f1f5e1 Mon Sep 17 00:00:00 2001 From: CloneWith Date: Thu, 11 Dec 2025 14:59:07 +0800 Subject: [PATCH 2/3] Add association manager (wrapper) interface --- .../WindowsAssociationManagerAdapter.cs | 18 +++++++++++++++ osu.Game/Platform/IAssociationManager.cs | 23 +++++++++++++++++++ 2 files changed, 41 insertions(+) create mode 100644 osu.Desktop/Windows/WindowsAssociationManagerAdapter.cs create mode 100644 osu.Game/Platform/IAssociationManager.cs diff --git a/osu.Desktop/Windows/WindowsAssociationManagerAdapter.cs b/osu.Desktop/Windows/WindowsAssociationManagerAdapter.cs new file mode 100644 index 000000000000..b2137fef29fa --- /dev/null +++ b/osu.Desktop/Windows/WindowsAssociationManagerAdapter.cs @@ -0,0 +1,18 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Runtime.Versioning; +using osu.Game.Platform; + +namespace osu.Desktop.Windows +{ + [SupportedOSPlatform("windows")] + public class WindowsAssociationManagerAdapter : IAssociationManager + { + public void InstallAssociations() => WindowsAssociationManager.InstallAssociations(); + + public void UpdateAssociations() => WindowsAssociationManager.UpdateAssociations(); + + public void UninstallAssociations() => WindowsAssociationManager.UninstallAssociations(); + } +} diff --git a/osu.Game/Platform/IAssociationManager.cs b/osu.Game/Platform/IAssociationManager.cs new file mode 100644 index 000000000000..11c7505e48e6 --- /dev/null +++ b/osu.Game/Platform/IAssociationManager.cs @@ -0,0 +1,23 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +namespace osu.Game.Platform +{ + public interface IAssociationManager + { + /// + /// Installs or refreshes file and URI protocol associations for this client. + /// + void InstallAssociations(); + + /// + /// Updates associations with latest definitions. + /// + void UpdateAssociations(); + + /// + /// Remove all associations with the client. + /// + void UninstallAssociations(); + } +} From 1a5edab4325359efd58933c20aabeb66b37cfb11 Mon Sep 17 00:00:00 2001 From: CloneWith Date: Thu, 11 Dec 2025 15:00:27 +0800 Subject: [PATCH 3/3] Add settings entry for association update --- osu.Desktop/OsuGameDesktop.cs | 8 +++++ osu.Game/Localisation/DebugSettingsStrings.cs | 14 ++++++++ .../Maintenance/AssociationPrePromptDialog.cs | 33 +++++++++++++++++++ .../Sections/Maintenance/GeneralSettings.cs | 14 +++++++- 4 files changed, 68 insertions(+), 1 deletion(-) create mode 100644 osu.Game/Overlays/Settings/Sections/Maintenance/AssociationPrePromptDialog.cs diff --git a/osu.Desktop/OsuGameDesktop.cs b/osu.Desktop/OsuGameDesktop.cs index 885ee0620eed..cf8ebf88ace7 100644 --- a/osu.Desktop/OsuGameDesktop.cs +++ b/osu.Desktop/OsuGameDesktop.cs @@ -22,6 +22,7 @@ using osu.Game.IPC; using osu.Game.Online.Multiplayer; using osu.Game.Performance; +using osu.Game.Platform; using osu.Game.Utils; namespace osu.Desktop @@ -34,11 +35,18 @@ internal partial class OsuGameDesktop : OsuGame [Cached(typeof(IHighPerformanceSessionManager))] private readonly HighPerformanceSessionManager highPerformanceSessionManager = new HighPerformanceSessionManager(); + [Cached(typeof(IAssociationManager))] + private readonly IAssociationManager? associationManager; + public bool IsFirstRun { get; init; } public OsuGameDesktop(string[]? args = null) : base(args) { + if (OperatingSystem.IsWindows()) + { + associationManager = new WindowsAssociationManagerAdapter(); + } } public override StableStorage? GetStorageForStableInstall() diff --git a/osu.Game/Localisation/DebugSettingsStrings.cs b/osu.Game/Localisation/DebugSettingsStrings.cs index bdb034898106..7dcb6c037265 100644 --- a/osu.Game/Localisation/DebugSettingsStrings.cs +++ b/osu.Game/Localisation/DebugSettingsStrings.cs @@ -19,6 +19,20 @@ public static class DebugSettingsStrings /// public static LocalisableString RunLatencyCertifier => new TranslatableString(getKey(@"run_latency_certifier"), @"Run latency certifier"); + /// + /// "Set application association" + /// + public static LocalisableString SetApplicationAssociation => new TranslatableString(getKey(@"set_application_association"), @"Set application association"); + + /// + /// "Association Update" + /// + public static LocalisableString AssociationDialogHeader => new TranslatableString(getKey(@"association_dialog_header"), @"Association Update"); + + public static LocalisableString AssociationDialogText => new TranslatableString(getKey(@"association_dialog_text"), + "Setting up a separate association for osu!gu client." + + "\nPlease be aware that associations for the official client won't be changed. You may need to reinstall or update it to fix them."); + private static string getKey(string key) => $"{prefix}:{key}"; } } diff --git a/osu.Game/Overlays/Settings/Sections/Maintenance/AssociationPrePromptDialog.cs b/osu.Game/Overlays/Settings/Sections/Maintenance/AssociationPrePromptDialog.cs new file mode 100644 index 000000000000..e5d1af63122a --- /dev/null +++ b/osu.Game/Overlays/Settings/Sections/Maintenance/AssociationPrePromptDialog.cs @@ -0,0 +1,33 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using osu.Framework.Graphics.Sprites; +using osu.Game.Localisation; +using osu.Game.Overlays.Dialog; + +namespace osu.Game.Overlays.Settings.Sections.Maintenance +{ + public partial class AssociationPrePromptDialog : PopupDialog + { + public AssociationPrePromptDialog(Action setAssociation) + { + HeaderText = DebugSettingsStrings.AssociationDialogHeader; + BodyText = DebugSettingsStrings.AssociationDialogText; + Icon = FontAwesome.Solid.InfoCircle; + + Buttons = new PopupDialogButton[] + { + new PopupDialogOkButton + { + Text = DialogStrings.Confirm, + Action = setAssociation, + }, + new PopupDialogCancelButton + { + Text = DialogStrings.Cancel, + }, + }; + } + } +} diff --git a/osu.Game/Overlays/Settings/Sections/Maintenance/GeneralSettings.cs b/osu.Game/Overlays/Settings/Sections/Maintenance/GeneralSettings.cs index 47314dcafef4..584e2075f260 100644 --- a/osu.Game/Overlays/Settings/Sections/Maintenance/GeneralSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Maintenance/GeneralSettings.cs @@ -3,12 +3,14 @@ using System.Linq; using System.Threading.Tasks; +using osu.Framework; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Localisation; using osu.Framework.Platform; using osu.Framework.Screens; using osu.Game.Localisation; +using osu.Game.Platform; using osu.Game.Screens; using osu.Game.Screens.Import; using osu.Game.Screens.Utility; @@ -22,7 +24,8 @@ public partial class GeneralSettings : SettingsSubsection private ISystemFileSelector? selector; [BackgroundDependencyLoader] - private void load(OsuGameBase game, GameHost host, IPerformFromScreenRunner? performer) + private void load(OsuGameBase game, GameHost host, IPerformFromScreenRunner? performer, + IAssociationManager? associationManager, IDialogOverlay? dialogOverlay) { if ((selector = host.CreateSystemFileSelector(game.HandledExtensions.ToArray())) != null) selector.Selected += f => Task.Run(() => game.Import(f.FullName)); @@ -46,6 +49,15 @@ private void load(OsuGameBase game, GameHost host, IPerformFromScreenRunner? per Action = () => performer?.PerformFromScreen(menu => menu.Push(new LatencyCertifierScreen())) } }); + + if (RuntimeInfo.OS is RuntimeInfo.Platform.Windows && associationManager != null) + { + Add(new SettingsButton + { + Text = DebugSettingsStrings.SetApplicationAssociation, + Action = () => dialogOverlay?.Push(new AssociationPrePromptDialog(associationManager.InstallAssociations)), + }); + } } } }