diff --git a/samples/ControlCatalog/Pages/PlatformInfoPage.xaml.cs b/samples/ControlCatalog/Pages/PlatformInfoPage.xaml.cs index 1f374517827..6325af26889 100644 --- a/samples/ControlCatalog/Pages/PlatformInfoPage.xaml.cs +++ b/samples/ControlCatalog/Pages/PlatformInfoPage.xaml.cs @@ -1,5 +1,8 @@ -using Avalonia.Controls; +using System; +using Avalonia.Controls; using Avalonia.Markup.Xaml; +using Avalonia.Markup.Xaml.MarkupExtensions; +using Avalonia.Media.Immutable; using ControlCatalog.ViewModels; namespace ControlCatalog.Pages diff --git a/samples/ControlCatalog/ViewModels/PlatformInformationViewModel.cs b/samples/ControlCatalog/ViewModels/PlatformInformationViewModel.cs index e4f6c3ac73a..60f1f84cc91 100644 --- a/samples/ControlCatalog/ViewModels/PlatformInformationViewModel.cs +++ b/samples/ControlCatalog/ViewModels/PlatformInformationViewModel.cs @@ -1,3 +1,5 @@ +using System; +using System.Runtime.InteropServices; using Avalonia; using Avalonia.Platform; using MiniMvvm; @@ -13,7 +15,7 @@ public PlatformInformationViewModel() if (runtimeInfo is { } info) { - if (info.IsBrowser) + if (RuntimeInformation.IsOSPlatform(OSPlatform.Create("BROWSER"))) { if (info.IsDesktop) { diff --git a/src/Avalonia.Base/Avalonia.Base.csproj b/src/Avalonia.Base/Avalonia.Base.csproj index 94a9ea83524..d6f1542687c 100644 --- a/src/Avalonia.Base/Avalonia.Base.csproj +++ b/src/Avalonia.Base/Avalonia.Base.csproj @@ -28,10 +28,13 @@ + + + diff --git a/src/Avalonia.Base/Compatibility/OperatingSystem.cs b/src/Avalonia.Base/Compatibility/OperatingSystem.cs new file mode 100644 index 00000000000..838f7da8b22 --- /dev/null +++ b/src/Avalonia.Base/Compatibility/OperatingSystem.cs @@ -0,0 +1,26 @@ +using System; +using System.Runtime.InteropServices; + +namespace Avalonia.Compatibility +{ + internal sealed class OperatingSystemEx + { +#if NET6_0_OR_GREATER + public static bool IsWindows() => OperatingSystem.IsWindows(); + public static bool IsMacOS() => OperatingSystem.IsMacOS(); + public static bool IsLinux() => OperatingSystem.IsLinux(); + public static bool IsAndroid() => OperatingSystem.IsAndroid(); + public static bool IsIOS() => OperatingSystem.IsIOS(); + public static bool IsBrowser() => OperatingSystem.IsBrowser(); + public static bool IsOSPlatform(string platform) => OperatingSystem.IsOSPlatform(platform); +#else + public static bool IsWindows() => RuntimeInformation.IsOSPlatform(OSPlatform.Windows); + public static bool IsMacOS() => RuntimeInformation.IsOSPlatform(OSPlatform.OSX); + public static bool IsLinux() => RuntimeInformation.IsOSPlatform(OSPlatform.Linux); + public static bool IsAndroid() => IsOSPlatform("ANDROID"); + public static bool IsIOS() => IsOSPlatform("IOS"); + public static bool IsBrowser() => IsOSPlatform("BROWSER"); + public static bool IsOSPlatform(string platform) => RuntimeInformation.IsOSPlatform(OSPlatform.Create(platform)); +#endif + } +} diff --git a/src/Avalonia.Base/Logging/LogArea.cs b/src/Avalonia.Base/Logging/LogArea.cs index 972a9a1e9db..f15e87da1b2 100644 --- a/src/Avalonia.Base/Logging/LogArea.cs +++ b/src/Avalonia.Base/Logging/LogArea.cs @@ -35,6 +35,11 @@ public static class LogArea /// public const string Control = nameof(Control); + /// + /// The log event comes from Win32 Platform. + /// + public const string Platform = nameof(Platform); + /// /// The log event comes from Win32 Platform. /// diff --git a/src/Avalonia.Base/Platform/IRuntimePlatform.cs b/src/Avalonia.Base/Platform/IRuntimePlatform.cs index 91d2a1e0cf7..64b504c479d 100644 --- a/src/Avalonia.Base/Platform/IRuntimePlatform.cs +++ b/src/Avalonia.Base/Platform/IRuntimePlatform.cs @@ -23,29 +23,10 @@ public interface IUnmanagedBlob : IDisposable [Unstable] public record struct RuntimePlatformInfo { - public OperatingSystemType OperatingSystem { get; set; } - public FormFactorType FormFactor => IsDesktop ? FormFactorType.Desktop : IsMobile ? FormFactorType.Mobile : FormFactorType.Unknown; public bool IsDesktop { get; set; } public bool IsMobile { get; set; } - public bool IsBrowser { get; set; } - public bool IsCoreClr { get; set; } - public bool IsMono { get; set; } - public bool IsDotNetFramework { get; set; } - public bool IsUnix { get; set; } - } - - [Unstable] - public enum OperatingSystemType - { - Unknown, - WinNT, - Linux, - OSX, - Android, - iOS, - Browser } [Unstable] diff --git a/src/Avalonia.Base/Platform/StandardRuntimePlatform.cs b/src/Avalonia.Base/Platform/StandardRuntimePlatform.cs index 4df9e8e917f..8352d794d08 100644 --- a/src/Avalonia.Base/Platform/StandardRuntimePlatform.cs +++ b/src/Avalonia.Base/Platform/StandardRuntimePlatform.cs @@ -1,6 +1,6 @@ using System; -using System.Runtime.InteropServices; using System.Threading; +using Avalonia.Compatibility; using Avalonia.Platform.Internal; namespace Avalonia.Platform @@ -14,45 +14,13 @@ public IDisposable StartSystemTimer(TimeSpan interval, Action tick) public IUnmanagedBlob AllocBlob(int size) => new UnmanagedBlob(size); - private static readonly Lazy Info = new(() => + private static readonly RuntimePlatformInfo s_info = new() { - OperatingSystemType os; + IsDesktop = OperatingSystemEx.IsWindows() || OperatingSystemEx.IsMacOS() || OperatingSystemEx.IsLinux(), + IsMobile = OperatingSystemEx.IsAndroid() || OperatingSystemEx.IsIOS() + }; - if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) - os = OperatingSystemType.OSX; - else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) - os = OperatingSystemType.Linux; - else if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) - os = OperatingSystemType.WinNT; - else if (RuntimeInformation.IsOSPlatform(OSPlatform.Create("Android"))) - os = OperatingSystemType.Android; - else if (RuntimeInformation.IsOSPlatform(OSPlatform.Create("iOS"))) - os = OperatingSystemType.iOS; - else if (RuntimeInformation.IsOSPlatform(OSPlatform.Create("Browser"))) - os = OperatingSystemType.Browser; - else - throw new Exception("Unknown OS platform " + RuntimeInformation.OSDescription); - // Source: https://github.com/dotnet/runtime/blob/main/src/libraries/Common/tests/TestUtilities/System/PlatformDetection.cs - var isCoreClr = Environment.Version.Major >= 5 || RuntimeInformation.FrameworkDescription.StartsWith(".NET Core", StringComparison.OrdinalIgnoreCase); - var isMonoRuntime = Type.GetType("Mono.Runtime") != null; - var isFramework = !isCoreClr && RuntimeInformation.FrameworkDescription.StartsWith(".NET Framework", StringComparison.OrdinalIgnoreCase); - - return new RuntimePlatformInfo - { - IsCoreClr = isCoreClr, - IsDotNetFramework = isFramework, - IsMono = isMonoRuntime, - - IsDesktop = os is OperatingSystemType.Linux or OperatingSystemType.OSX or OperatingSystemType.WinNT, - IsMobile = os is OperatingSystemType.Android or OperatingSystemType.iOS, - IsUnix = os is OperatingSystemType.Linux or OperatingSystemType.OSX or OperatingSystemType.Android, - IsBrowser = os == OperatingSystemType.Browser, - OperatingSystem = os, - }; - }); - - - public virtual RuntimePlatformInfo GetRuntimeInfo() => Info.Value; + public virtual RuntimePlatformInfo GetRuntimeInfo() => s_info; } } diff --git a/src/Avalonia.Base/Platform/StandardRuntimePlatformServices.cs b/src/Avalonia.Base/Platform/StandardRuntimePlatformServices.cs index 65d67333994..0a36b4c9dd4 100644 --- a/src/Avalonia.Base/Platform/StandardRuntimePlatformServices.cs +++ b/src/Avalonia.Base/Platform/StandardRuntimePlatformServices.cs @@ -1,4 +1,5 @@ using System.Reflection; +using Avalonia.Compatibility; using Avalonia.Platform.Internal; using Avalonia.Platform.Interop; @@ -18,15 +19,9 @@ public static void Register(Assembly? assembly = null) #if NET6_0_OR_GREATER new Net6Loader() #else - standardPlatform.GetRuntimeInfo().OperatingSystem switch - { - OperatingSystemType.WinNT => (IDynamicLibraryLoader)new Win32Loader(), - OperatingSystemType.OSX => new UnixLoader(), - OperatingSystemType.Linux => new UnixLoader(), - OperatingSystemType.Android => new UnixLoader(), - // iOS, WASM, ... - _ => new NotSupportedLoader() - } + OperatingSystemEx.IsWindows() ? (IDynamicLibraryLoader)new Win32Loader() + : OperatingSystemEx.IsMacOS() || OperatingSystemEx.IsLinux() || OperatingSystemEx.IsAndroid() ? new UnixLoader() + : new NotSupportedLoader() #endif ); } diff --git a/src/Avalonia.Desktop/AppBuilderDesktopExtensions.cs b/src/Avalonia.Desktop/AppBuilderDesktopExtensions.cs index 195af7b46e6..13a4ac51d21 100644 --- a/src/Avalonia.Desktop/AppBuilderDesktopExtensions.cs +++ b/src/Avalonia.Desktop/AppBuilderDesktopExtensions.cs @@ -1,5 +1,6 @@ +using Avalonia.Compatibility; using Avalonia.Controls; -using Avalonia.Platform; +using Avalonia.Logging; namespace Avalonia { @@ -7,8 +8,6 @@ public static class AppBuilderDesktopExtensions { public static AppBuilder UsePlatformDetect(this AppBuilder builder) { - var os = builder.RuntimePlatform.GetRuntimeInfo().OperatingSystem; - // We don't have the ability to load every assembly right now, so we are // stuck with manual configuration here // Helpers are extracted to separate methods to take the advantage of the fact @@ -16,21 +15,27 @@ public static AppBuilder UsePlatformDetect(this AppBuilder builder) // Additionally, by having a hard reference to each assembly, // we verify that the assemblies are in the final .deps.json file // so .NET Core knows where to load the assemblies from,. - if (os == OperatingSystemType.WinNT) + if (OperatingSystemEx.IsWindows()) { LoadWin32(builder); LoadSkia(builder); } - else if(os==OperatingSystemType.OSX) + else if(OperatingSystemEx.IsMacOS()) { LoadAvaloniaNative(builder); LoadSkia(builder); } - else + else if (OperatingSystemEx.IsLinux()) { LoadX11(builder); LoadSkia(builder); } + else + { + Logger.TryGet(LogEventLevel.Warning, LogArea.Platform)?.Log(builder, + "Avalonia.Desktop package was referenced on non-desktop platform or it isn't supported"); + } + return builder; } diff --git a/src/Avalonia.OpenGL/Egl/EglInterface.cs b/src/Avalonia.OpenGL/Egl/EglInterface.cs index ad4b55a686c..a913c059963 100644 --- a/src/Avalonia.OpenGL/Egl/EglInterface.cs +++ b/src/Avalonia.OpenGL/Egl/EglInterface.cs @@ -1,5 +1,6 @@ using System; using System.Runtime.InteropServices; +using Avalonia.Compatibility; using Avalonia.Platform; using Avalonia.Platform.Interop; using Avalonia.SourceGenerator; @@ -24,10 +25,9 @@ public EglInterface() : this(Load()) static Func Load() { - var os = AvaloniaLocator.Current.GetService().GetRuntimeInfo().OperatingSystem; - if(os == OperatingSystemType.Linux) + if(OperatingSystemEx.IsLinux()) return Load("libEGL.so.1"); - if (os == OperatingSystemType.Android) + if (OperatingSystemEx.IsAndroid()) return Load("libEGL.so"); throw new PlatformNotSupportedException(); diff --git a/src/Browser/Avalonia.Browser/BrowserRuntimePlatform.cs b/src/Browser/Avalonia.Browser/BrowserRuntimePlatform.cs index 0abc7703dae..67bb0404102 100644 --- a/src/Browser/Avalonia.Browser/BrowserRuntimePlatform.cs +++ b/src/Browser/Avalonia.Browser/BrowserRuntimePlatform.cs @@ -11,12 +11,11 @@ internal class BrowserRuntimePlatform : StandardRuntimePlatform { private static readonly Lazy Info = new(() => { + var isMobile = AvaloniaModule.IsMobile(); var result = new RuntimePlatformInfo { - IsCoreClr = true, // WASM browser is always CoreCLR - IsBrowser = true, // BrowserRuntimePlatform only runs on Browser. - OperatingSystem = OperatingSystemType.Browser, - IsMobile = AvaloniaModule.IsMobile() + IsMobile = isMobile, + IsDesktop = !isMobile }; return result; diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlOptionMarkupExtensionTransformer.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlOptionMarkupExtensionTransformer.cs index 5004e594f7b..13de455b962 100644 --- a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlOptionMarkupExtensionTransformer.cs +++ b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlOptionMarkupExtensionTransformer.cs @@ -366,6 +366,7 @@ public void EmitCall(XamlEmitContext conte foreach (var branch in ExtensionNodeContainer.Branches) { var next = codeGen.DefineLabel(); + codeGen.Emit(OpCodes.Nop); if (branch.HasContext) { codeGen.Ldloc(context.ContextLocal); diff --git a/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/OnFormFactorExtension.cs b/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/OnFormFactorExtension.cs index 51e09eef716..c2f1100eff8 100644 --- a/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/OnFormFactorExtension.cs +++ b/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/OnFormFactorExtension.cs @@ -6,7 +6,7 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions; -public class OnFormFactorExtension : OnFormFactorExtensionBase +public sealed class OnFormFactorExtension : OnFormFactorExtensionBase { public OnFormFactorExtension() { @@ -24,7 +24,7 @@ public static bool ShouldProvideOption(IServiceProvider serviceProvider, FormFac } } -public class OnFormFactorExtension : OnFormFactorExtensionBase> +public sealed class OnFormFactorExtension : OnFormFactorExtensionBase> { public OnFormFactorExtension() { diff --git a/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/OnPlatformExtension.cs b/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/OnPlatformExtension.cs index 1ac7a522f15..1c20020978e 100644 --- a/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/OnPlatformExtension.cs +++ b/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/OnPlatformExtension.cs @@ -1,11 +1,11 @@ #nullable enable using System; +using Avalonia.Compatibility; using Avalonia.Metadata; -using Avalonia.Platform; namespace Avalonia.Markup.Xaml.MarkupExtensions; -public class OnPlatformExtension : OnPlatformExtensionBase +public sealed class OnPlatformExtension : OnPlatformExtensionBase { public OnPlatformExtension() { @@ -17,13 +17,13 @@ public OnPlatformExtension(object defaultValue) Default = defaultValue; } - public static bool ShouldProvideOption(IServiceProvider serviceProvider, OperatingSystemType option) + public static bool ShouldProvideOption(string option) { - return serviceProvider.GetService().GetRuntimeInfo().OperatingSystem == option; + return ShouldProvideOptionInternal(option); } } -public class OnPlatformExtension : OnPlatformExtensionBase> +public sealed class OnPlatformExtension : OnPlatformExtensionBase> { public OnPlatformExtension() { @@ -35,9 +35,9 @@ public OnPlatformExtension(TReturn defaultValue) Default = defaultValue; } - public static bool ShouldProvideOption(IServiceProvider serviceProvider, OperatingSystemType option) + public static bool ShouldProvideOption(string option) { - return serviceProvider.GetService().GetRuntimeInfo().OperatingSystem == option; + return ShouldProvideOptionInternal(option); } } @@ -47,27 +47,44 @@ public abstract class OnPlatformExtensionBase : IAddChild [MarkupExtensionDefaultOption] public TReturn? Default { get; set; } - [MarkupExtensionOption(OperatingSystemType.WinNT)] + [MarkupExtensionOption("WINDOWS")] public TReturn? Windows { get; set; } - [MarkupExtensionOption(OperatingSystemType.OSX)] + [MarkupExtensionOption("OSX")] // ReSharper disable once InconsistentNaming public TReturn? macOS { get; set; } - [MarkupExtensionOption(OperatingSystemType.Linux)] + [MarkupExtensionOption("LINUX")] public TReturn? Linux { get; set; } - [MarkupExtensionOption(OperatingSystemType.Android)] + [MarkupExtensionOption("ANDROID")] public TReturn? Android { get; set; } - [MarkupExtensionOption(OperatingSystemType.iOS)] + [MarkupExtensionOption("IOS")] // ReSharper disable once InconsistentNaming public TReturn? iOS { get; set; } - [MarkupExtensionOption(OperatingSystemType.Browser)] + [MarkupExtensionOption("BROWSER")] public TReturn? Browser { get; set; } // Required for the compiler, will be replaced with actual method compile time. public object ProvideValue() { return this; } void IAddChild.AddChild(TOn child) {} + + private protected static bool ShouldProvideOptionInternal(string option) + { + // Instead of using OperatingSystem.IsOSPlatform(string) we use specific "Is***" methods so whole method can be trimmed by the mono linked. + // Keep in mind it works only with const "option" parameter. + // IsOSPlatform might work better with trimming in the future, so it should be re-visited after .NET 8/9. + return option switch + { + "WINDOWS" => OperatingSystemEx.IsWindows(), + "OSX" => OperatingSystemEx.IsMacOS(), + "LINUX" => OperatingSystemEx.IsLinux(), + "ANDROID" => OperatingSystemEx.IsAndroid(), + "IOS" => OperatingSystemEx.IsIOS(), + "BROWSER" => OperatingSystemEx.IsBrowser(), + _ => OperatingSystemEx.IsOSPlatform(option) + }; + } } diff --git a/src/Skia/Avalonia.Skia/Helpers/PixelFormatHelper.cs b/src/Skia/Avalonia.Skia/Helpers/PixelFormatHelper.cs index 9236bdee8d0..84a1972e013 100644 --- a/src/Skia/Avalonia.Skia/Helpers/PixelFormatHelper.cs +++ b/src/Skia/Avalonia.Skia/Helpers/PixelFormatHelper.cs @@ -1,4 +1,5 @@ -using Avalonia.Platform; +using Avalonia.Compatibility; +using Avalonia.Platform; using SkiaSharp; namespace Avalonia.Skia.Helpers @@ -18,10 +19,7 @@ public static SKColorType ResolveColorType(PixelFormat? format) var colorType = format?.ToSkColorType() ?? SKImageInfo.PlatformColorType; // TODO: This looks like some leftover hack - var runtimePlatform = AvaloniaLocator.Current?.GetService(); - var runtime = runtimePlatform?.GetRuntimeInfo(); - - if (runtime?.IsDesktop == true && runtime.Value.OperatingSystem == OperatingSystemType.Linux) + if (OperatingSystemEx.IsLinux()) { colorType = SKColorType.Bgra8888; } diff --git a/tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/OnPlatformExtensionTests.cs b/tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/OnPlatformExtensionTests.cs deleted file mode 100644 index 1d37378010e..00000000000 --- a/tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/OnPlatformExtensionTests.cs +++ /dev/null @@ -1,75 +0,0 @@ -using Avalonia.Controls; -using Avalonia.Platform; -using Xunit; - -namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions; - -public class OnPlatformExtensionTests : XamlTestBase -{ - [Fact] - public void Should_Resolve_Default_Value() - { - using (AvaloniaLocator.EnterScope()) - { - AvaloniaLocator.CurrentMutable.Bind() - .ToConstant(new TestRuntimePlatform(OperatingSystemType.Unknown)); - - var xaml = @" - - -"; - - var userControl = (UserControl)AvaloniaRuntimeXamlLoader.Load(xaml); - var textBlock = (TextBlock)userControl.Content!; - - Assert.Equal("Hello World", textBlock.Text); - } - } - - [Theory] - [InlineData(OperatingSystemType.WinNT, "Im Windows")] - [InlineData(OperatingSystemType.OSX, "Im macOS")] - [InlineData(OperatingSystemType.Linux, "Im Linux")] - [InlineData(OperatingSystemType.Android, "Im Android")] - [InlineData(OperatingSystemType.iOS, "Im iOS")] - [InlineData(OperatingSystemType.Browser, "Im Browser")] - [InlineData(OperatingSystemType.Unknown, "Default value")] - public void Should_Resolve_Expected_Value_Per_Platform(OperatingSystemType currentPlatform, string expectedResult) - { - using (AvaloniaLocator.EnterScope()) - { - AvaloniaLocator.CurrentMutable.Bind() - .ToConstant(new TestRuntimePlatform(currentPlatform)); - - var xaml = @" - - -"; - - var userControl = (UserControl)AvaloniaRuntimeXamlLoader.Load(xaml); - var textBlock = (TextBlock)userControl.Content!; - - Assert.Equal(expectedResult, textBlock.Text); - } - } - - private class TestRuntimePlatform : StandardRuntimePlatform - { - private readonly OperatingSystemType _operatingSystemType; - - public TestRuntimePlatform(OperatingSystemType operatingSystemType) - { - _operatingSystemType = operatingSystemType; - } - - public override RuntimePlatformInfo GetRuntimeInfo() - { - return new RuntimePlatformInfo() { OperatingSystem = _operatingSystemType }; - } - } -} diff --git a/tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/OptionsMarkupExtensionTests.cs b/tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/OptionsMarkupExtensionTests.cs index 2d1f9617430..0c67f385e1d 100644 --- a/tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/OptionsMarkupExtensionTests.cs +++ b/tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/OptionsMarkupExtensionTests.cs @@ -434,7 +434,7 @@ public void Resolve_Expected_Value_With_Method_Without_ServiceProvider() "; + Text='{local:OptionsMarkupExtensionNoServiceProvider OptionB=""Im Option 2"", OptionA=""Im Option 1""}' />"; var textBlock = (TextBlock)AvaloniaRuntimeXamlLoader.Load(xaml);