diff --git a/osu.Desktop/OsuGameDesktop.cs b/osu.Desktop/OsuGameDesktop.cs index 2b232db274b1..f92e0c659a5a 100644 --- a/osu.Desktop/OsuGameDesktop.cs +++ b/osu.Desktop/OsuGameDesktop.cs @@ -145,6 +145,9 @@ protected override void LoadComplete() LoadComponentAsync(new ElevatedPrivilegesChecker(), Add); + if (OperatingSystem.IsWindows()) + LoadComponentAsync(new CompatibilityModeChecker(), Add); + osuSchemeLinkIPCChannel = new OsuSchemeLinkIPCChannel(Host, this); archiveImportIPCChannel = new ArchiveImportIPCChannel(Host, this); } diff --git a/osu.Desktop/Program.cs b/osu.Desktop/Program.cs index 73670adc495d..5d6243cd50bd 100644 --- a/osu.Desktop/Program.cs +++ b/osu.Desktop/Program.cs @@ -52,15 +52,24 @@ public static void Main(string[] args) // See https://www.mongodb.com/docs/realm/sdk/dotnet/compatibility/ if (windowsVersion.Major < 6 || (windowsVersion.Major == 6 && windowsVersion.Minor <= 2)) { - // If users running in compatibility mode becomes more of a common thing, we may want to provide better guidance or even consider - // disabling it ourselves. - // We could also better detect compatibility mode if required: - // https://stackoverflow.com/questions/10744651/how-i-can-detect-if-my-application-is-running-under-compatibility-mode#comment58183249_10744730 - SDL.SDL_ShowSimpleMessageBox(SDL.SDL_MessageBoxFlags.SDL_MESSAGEBOX_ERROR, - "Your operating system is too old to run osu!", - "This version of osu! requires at least Windows 8.1 to run.\n" - + "Please upgrade your operating system or consider using an older version of osu!.\n\n" - + "If you are running a newer version of windows, please check you don't have \"Compatibility mode\" turned on for osu!", IntPtr.Zero); + bool isInCompatibilityMode = CompatibilityModeChecker.CheckCompatibilityMode(); + + if (isInCompatibilityMode) + { + SDL.SDL_ShowSimpleMessageBox(SDL.SDL_MessageBoxFlags.SDL_MESSAGEBOX_ERROR, + "osu! is running in compatibility mode", + "osu! is running in compatibility mode. This may cause issues with the game. Please ensure osu! is not set to run in compatibility mode.\n\n" + + "Debug information: " + CompatibilityModeChecker.GetCompatibilityFlags(), IntPtr.Zero); + } + else + { + SDL.SDL_ShowSimpleMessageBox(SDL.SDL_MessageBoxFlags.SDL_MESSAGEBOX_ERROR, + "Your operating system is too old to run osu!", + "This version of osu! requires at least Windows 8.1 to run.\n" + + "Please upgrade your operating system or consider using an older version of osu!.\n\n" + + "If you are running a newer version of windows, please check you don't have \"Compatibility mode\" turned on for osu!", IntPtr.Zero); + } + return; } diff --git a/osu.Desktop/Windows/CompatibilityModeChecker.cs b/osu.Desktop/Windows/CompatibilityModeChecker.cs new file mode 100644 index 000000000000..9ce9470257bb --- /dev/null +++ b/osu.Desktop/Windows/CompatibilityModeChecker.cs @@ -0,0 +1,84 @@ +// 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 System.Linq; +using System.Runtime.Versioning; +using Microsoft.Win32; +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Sprites; +using osu.Framework.Logging; +using osu.Game.Graphics; +using osu.Game.Localisation; +using osu.Game.Overlays; +using osu.Game.Overlays.Notifications; + +namespace osu.Desktop.Windows +{ + /// + /// Checks if the game is running with windows compatibility optimizations which could cause issues. Displays a warning notification if so. + /// + [SupportedOSPlatform("windows")] + public partial class CompatibilityModeChecker : Component + { + [Resolved] + private INotificationOverlay notifications { get; set; } = null!; + + [BackgroundDependencyLoader] + private void load() + { + if (!CheckCompatibilityMode()) return; + + notifications.Post(new CompatibilityModeNotification()); + LogCompatibilityFlags(); + } + + /// + /// Check if the game is running with windows compatibility optimizations + /// + /// Whether compatibility mode flags are detected or not + public static bool CheckCompatibilityMode() + { + using var layers = Registry.CurrentUser.OpenSubKey(@"Software\Microsoft\Windows NT\CurrentVersion\AppCompatFlags\Layers"); + + return layers != null && layers.GetValueNames().Any(name => name.Equals(Environment.ProcessPath, StringComparison.OrdinalIgnoreCase)); + } + + /// + /// Log the compatibility flags for the current process if they exist + /// + public static void LogCompatibilityFlags() + { + Logger.Log($"Compatibility flags for {Environment.ProcessPath}: {GetCompatibilityFlags()}"); + } + + /// + /// Get the compatibility flags for the current process if they exist + /// + /// The compatibility flags + public static string GetCompatibilityFlags() + { + using var layers = Registry.CurrentUser.OpenSubKey(@"Software\Microsoft\Windows NT\CurrentVersion\AppCompatFlags\Layers"); + + return layers?.GetValue(Environment.ProcessPath) as string ?? string.Empty; + } + + private partial class CompatibilityModeNotification : SimpleNotification + { + public override bool IsImportant => true; + + public CompatibilityModeNotification() + { + Text = WindowsCompatibilityModeCheckerStrings.NotificationText; + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + Icon = FontAwesome.Solid.ShieldAlt; + IconContent.Colour = colours.YellowDark; + } + } + } +} diff --git a/osu.Game/Localisation/WindowsCompatibilityModeCheckerStrings.cs b/osu.Game/Localisation/WindowsCompatibilityModeCheckerStrings.cs new file mode 100644 index 000000000000..726a7163695e --- /dev/null +++ b/osu.Game/Localisation/WindowsCompatibilityModeCheckerStrings.cs @@ -0,0 +1,19 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Localisation; + +namespace osu.Game.Localisation +{ + public static class WindowsCompatibilityModeCheckerStrings + { + private const string prefix = @"osu.Game.Resources.Localisation.WindowsCompatibilityModeChecker"; + + /// + /// "osu! is running in compatibility mode. This may cause issues with the game. Please ensure osu! is not set to run in compatibility mode." + /// + public static LocalisableString NotificationText => new TranslatableString(getKey(@"notification_text"), @"osu! is running in compatibility mode. This may cause issues with the game. Please ensure osu! is not set to run in compatibility mode."); + + private static string getKey(string key) => $@"{prefix}:{key}"; + } +}