diff --git a/src/Android/Avalonia.Android/AndroidPlatform.cs b/src/Android/Avalonia.Android/AndroidPlatform.cs index 144909db4b..d8202064e7 100644 --- a/src/Android/Avalonia.Android/AndroidPlatform.cs +++ b/src/Android/Avalonia.Android/AndroidPlatform.cs @@ -20,6 +20,7 @@ namespace Avalonia public static AppBuilder UseAndroid(this AppBuilder builder) { return builder + .UseStandardRuntimePlatformSubsystem() .UseWindowingSubsystem(() => AndroidPlatform.Initialize(), "Android") .UseSkia(); } diff --git a/src/Avalonia.Base/Compatibility/NativeLibrary.cs b/src/Avalonia.Base/Compatibility/NativeLibrary.cs new file mode 100644 index 0000000000..7627c095bc --- /dev/null +++ b/src/Avalonia.Base/Compatibility/NativeLibrary.cs @@ -0,0 +1,122 @@ +using System; +using System.ComponentModel; +using System.Reflection; +using System.Runtime.InteropServices; +using Avalonia.Compatibility; +using Avalonia.Platform.Interop; + +namespace Avalonia.Compatibility +{ + internal class NativeLibraryEx + { +#if NET6_0_OR_GREATER + public static IntPtr Load(string dll, Assembly assembly) => NativeLibrary.Load(dll, assembly, null); + public static IntPtr Load(string dll) => NativeLibrary.Load(dll); + public static bool TryGetExport(IntPtr handle, string name, out IntPtr address) => + NativeLibrary.TryGetExport(handle, name, out address); +#else + public static IntPtr Load(string dll, Assembly assembly) => Load(dll); + public static IntPtr Load(string dll) + { + var handle = DlOpen!(dll); + if (handle != IntPtr.Zero) + return handle; + throw new InvalidOperationException("Unable to load " + dll, DlError!()); + } + + public static bool TryGetExport(IntPtr handle, string name, out IntPtr address) + { + try + { + address = DlSym!(handle, name); + return address != default; + } + catch (Exception) + { + address = default; + return false; + } + } + + static NativeLibraryEx() + { + if (OperatingSystemEx.IsWindows()) + { + Win32Imports.Init(); + } + else if (OperatingSystemEx.IsLinux() || OperatingSystemEx.IsMacOS()) + { + var buffer = Marshal.AllocHGlobal(0x1000); + uname(buffer); + var unixName = Marshal.PtrToStringAnsi(buffer); + Marshal.FreeHGlobal(buffer); + if (unixName == "Darwin") + OsXImports.Init(); + else + LinuxImports.Init(); + } + } + + private static Func? DlOpen; + private static Func? DlSym; + private static Func? DlError; + + [DllImport("libc")] + static extern int uname(IntPtr buf); + + static class Win32Imports + { + [DllImport("kernel32", CharSet = CharSet.Ansi, ExactSpelling = true, SetLastError = true)] + private static extern IntPtr GetProcAddress(IntPtr hModule, string procName); + + [DllImport("kernel32", EntryPoint = "LoadLibraryW", SetLastError = true, CharSet = CharSet.Unicode)] + private static extern IntPtr LoadLibrary(string lpszLib); + + public static void Init() + { + DlOpen = LoadLibrary; + DlSym = GetProcAddress; + DlError = () => new Win32Exception(Marshal.GetLastWin32Error()); + } + } + + static class LinuxImports + { + [DllImport("libdl.so.2")] + private static extern IntPtr dlopen(string path, int flags); + + [DllImport("libdl.so.2")] + private static extern IntPtr dlsym(IntPtr handle, string symbol); + + [DllImport("libdl.so.2")] + private static extern IntPtr dlerror(); + + public static void Init() + { + DlOpen = s => dlopen(s, 1); + DlSym = dlsym; + DlError = () => new InvalidOperationException(Marshal.PtrToStringAnsi(dlerror())); + } + } + + static class OsXImports + { + [DllImport("/usr/lib/libSystem.dylib")] + private static extern IntPtr dlopen(string path, int flags); + + [DllImport("/usr/lib/libSystem.dylib")] + private static extern IntPtr dlsym(IntPtr handle, string symbol); + + [DllImport("/usr/lib/libSystem.dylib")] + private static extern IntPtr dlerror(); + + public static void Init() + { + DlOpen = s => dlopen(s, 1); + DlSym = dlsym; + DlError = () => new InvalidOperationException(Marshal.PtrToStringAnsi(dlerror())); + } + } +#endif + } +} diff --git a/src/Avalonia.Base/Platform/IRuntimePlatform.cs b/src/Avalonia.Base/Platform/IRuntimePlatform.cs index 64b504c479..c6efadc99a 100644 --- a/src/Avalonia.Base/Platform/IRuntimePlatform.cs +++ b/src/Avalonia.Base/Platform/IRuntimePlatform.cs @@ -3,24 +3,13 @@ using Avalonia.Metadata; namespace Avalonia.Platform { - [Unstable] + [PrivateApi] public interface IRuntimePlatform { - IDisposable StartSystemTimer(TimeSpan interval, Action tick); RuntimePlatformInfo GetRuntimeInfo(); - IUnmanagedBlob AllocBlob(int size); } - [Unstable] - public interface IUnmanagedBlob : IDisposable - { - IntPtr Address { get; } - int Size { get; } - bool IsDisposed { get; } - - } - - [Unstable] + [PrivateApi] public record struct RuntimePlatformInfo { public FormFactorType FormFactor => IsDesktop ? FormFactorType.Desktop : @@ -29,7 +18,6 @@ namespace Avalonia.Platform public bool IsMobile { get; set; } } - [Unstable] public enum FormFactorType { Unknown, diff --git a/src/Avalonia.Base/Platform/Internal/DynLoader.cs b/src/Avalonia.Base/Platform/Internal/DynLoader.cs deleted file mode 100644 index 07903669b1..0000000000 --- a/src/Avalonia.Base/Platform/Internal/DynLoader.cs +++ /dev/null @@ -1,161 +0,0 @@ -using System; -using System.Runtime.InteropServices; -using Avalonia.Platform.Interop; - -// ReSharper disable InconsistentNaming -namespace Avalonia.Platform.Internal -{ - class UnixLoader : IDynamicLibraryLoader - { - static class LinuxImports - { - [DllImport("libdl.so.2")] - private static extern IntPtr dlopen(string path, int flags); - - [DllImport("libdl.so.2")] - private static extern IntPtr dlsym(IntPtr handle, string symbol); - - [DllImport("libdl.so.2")] - private static extern IntPtr dlerror(); - - public static void Init() - { - DlOpen = dlopen; - DlSym = dlsym; - DlError = dlerror; - } - } - - static class OsXImports - { - [DllImport("/usr/lib/libSystem.dylib")] - private static extern IntPtr dlopen(string path, int flags); - - [DllImport("/usr/lib/libSystem.dylib")] - private static extern IntPtr dlsym(IntPtr handle, string symbol); - - [DllImport("/usr/lib/libSystem.dylib")] - private static extern IntPtr dlerror(); - - public static void Init() - { - DlOpen = dlopen; - DlSym = dlsym; - DlError = dlerror; - } - - } - - - [DllImport("libc")] - static extern int uname(IntPtr buf); - - static UnixLoader() - { - var buffer = Marshal.AllocHGlobal(0x1000); - uname(buffer); - var unixName = Marshal.PtrToStringAnsi(buffer); - Marshal.FreeHGlobal(buffer); - if (unixName == "Darwin") - OsXImports.Init(); - else - LinuxImports.Init(); - } - - private static Func? DlOpen; - private static Func? DlSym; - private static Func? DlError; - // ReSharper restore InconsistentNaming - - static string? DlErrorString() => Marshal.PtrToStringAnsi(DlError!.Invoke()); - - public IntPtr LoadLibrary(string dll) - { - var handle = DlOpen!.Invoke(dll, 1); - if (handle == IntPtr.Zero) - throw new DynamicLibraryLoaderException(DlErrorString()!); - return handle; - } - - public IntPtr GetProcAddress(IntPtr dll, string proc, bool optional) - { - var ptr = DlSym!.Invoke(dll, proc); - if (ptr == IntPtr.Zero && !optional) - throw new DynamicLibraryLoaderException(DlErrorString()!); - return ptr; - } - } - - internal class Win32Loader : IDynamicLibraryLoader - { - [DllImport("kernel32", CharSet = CharSet.Ansi, ExactSpelling = true, SetLastError = true)] - private static extern IntPtr GetProcAddress(IntPtr hModule, string procName); - - [DllImport("kernel32", EntryPoint = "LoadLibraryW", SetLastError = true, CharSet = CharSet.Unicode)] - private static extern IntPtr LoadLibrary(string lpszLib); - - IntPtr IDynamicLibraryLoader.LoadLibrary(string dll) - { - var handle = LoadLibrary(dll); - if (handle != IntPtr.Zero) - return handle; - var err = Marshal.GetLastWin32Error(); - - throw new DynamicLibraryLoaderException("Error loading " + dll + " error " + err); - } - - IntPtr IDynamicLibraryLoader.GetProcAddress(IntPtr dll, string proc, bool optional) - { - var ptr = GetProcAddress(dll, proc); - if (ptr == IntPtr.Zero && !optional) - throw new DynamicLibraryLoaderException("Error " + Marshal.GetLastWin32Error()); - return ptr; - } - } - -#if NET6_0_OR_GREATER - internal class Net6Loader : IDynamicLibraryLoader - { - public IntPtr LoadLibrary(string dll) - { - try - { - return NativeLibrary.Load(dll); - } - catch (Exception ex) - { - throw new DynamicLibraryLoaderException("Error loading " + dll, ex); - } - } - - public IntPtr GetProcAddress(IntPtr dll, string proc, bool optional) - { - try - { - if (optional) - { - return NativeLibrary.TryGetExport(dll, proc, out var address) ? address : default; - } - return NativeLibrary.GetExport(dll, proc); - } - catch (Exception ex) - { - throw new DynamicLibraryLoaderException("Error " + dll, ex); - } - } - } -#endif - - internal class NotSupportedLoader : IDynamicLibraryLoader - { - IntPtr IDynamicLibraryLoader.LoadLibrary(string dll) - { - throw new PlatformNotSupportedException(); - } - - IntPtr IDynamicLibraryLoader.GetProcAddress(IntPtr dll, string proc, bool optional) - { - throw new PlatformNotSupportedException(); - } - } -} diff --git a/src/Avalonia.Base/Platform/Internal/UnmanagedBlob.cs b/src/Avalonia.Base/Platform/Internal/UnmanagedBlob.cs index a56d9ffd1c..fc299dbcec 100644 --- a/src/Avalonia.Base/Platform/Internal/UnmanagedBlob.cs +++ b/src/Avalonia.Base/Platform/Internal/UnmanagedBlob.cs @@ -6,7 +6,7 @@ using System.Threading; namespace Avalonia.Platform.Internal; -internal class UnmanagedBlob : IUnmanagedBlob +internal class UnmanagedBlob { private IntPtr _address; private readonly object _lock = new object(); diff --git a/src/Avalonia.Base/Platform/Interop/IDynamicLibraryLoader.cs b/src/Avalonia.Base/Platform/Interop/IDynamicLibraryLoader.cs deleted file mode 100644 index 56c50e340d..0000000000 --- a/src/Avalonia.Base/Platform/Interop/IDynamicLibraryLoader.cs +++ /dev/null @@ -1,24 +0,0 @@ -using System; -using Avalonia.Metadata; - -namespace Avalonia.Platform.Interop -{ - public interface IDynamicLibraryLoader - { - IntPtr LoadLibrary(string dll); - IntPtr GetProcAddress(IntPtr dll, string proc, bool optional); - } - - public class DynamicLibraryLoaderException : Exception - { - public DynamicLibraryLoaderException(string message) : base(message) - { - - } - - public DynamicLibraryLoaderException(string message, Exception innerException) : base(message, innerException) - { - - } - } -} diff --git a/src/Avalonia.Base/Platform/StandardRuntimePlatform.cs b/src/Avalonia.Base/Platform/StandardRuntimePlatform.cs index 8352d794d0..7a5c92c774 100644 --- a/src/Avalonia.Base/Platform/StandardRuntimePlatform.cs +++ b/src/Avalonia.Base/Platform/StandardRuntimePlatform.cs @@ -1,26 +1,20 @@ using System; using System.Threading; using Avalonia.Compatibility; +using Avalonia.Metadata; using Avalonia.Platform.Internal; namespace Avalonia.Platform { + [PrivateApi] public class StandardRuntimePlatform : IRuntimePlatform { - public IDisposable StartSystemTimer(TimeSpan interval, Action tick) - { - return new Timer(_ => tick(), null, interval, interval); - } - - public IUnmanagedBlob AllocBlob(int size) => new UnmanagedBlob(size); - private static readonly RuntimePlatformInfo s_info = new() { IsDesktop = OperatingSystemEx.IsWindows() || OperatingSystemEx.IsMacOS() || OperatingSystemEx.IsLinux(), IsMobile = OperatingSystemEx.IsAndroid() || OperatingSystemEx.IsIOS() }; - - + public virtual RuntimePlatformInfo GetRuntimeInfo() => s_info; } } diff --git a/src/Avalonia.Base/Platform/StandardRuntimePlatformServices.cs b/src/Avalonia.Base/Platform/StandardRuntimePlatformServices.cs index 800d9b390f..70919bc477 100644 --- a/src/Avalonia.Base/Platform/StandardRuntimePlatformServices.cs +++ b/src/Avalonia.Base/Platform/StandardRuntimePlatformServices.cs @@ -3,27 +3,15 @@ using Avalonia.Compatibility; using Avalonia.Platform.Internal; using Avalonia.Platform.Interop; -namespace Avalonia.Platform +namespace Avalonia.Platform; + +internal static class StandardRuntimePlatformServices { - public static class StandardRuntimePlatformServices + public static void Register(Assembly? assembly = null) { - public static void Register(Assembly? assembly = null) - { - var standardPlatform = new StandardRuntimePlatform(); - - AssetLoader.RegisterResUriParsers(); - AvaloniaLocator.CurrentMutable - .Bind().ToConstant(standardPlatform) - .Bind().ToConstant(new StandardAssetLoader(assembly)) - .Bind().ToConstant( -#if NET6_0_OR_GREATER - new Net6Loader() -#else - OperatingSystemEx.IsWindows() ? (IDynamicLibraryLoader)new Win32Loader() - : OperatingSystemEx.IsMacOS() || OperatingSystemEx.IsLinux() || OperatingSystemEx.IsAndroid() ? new UnixLoader() - : new NotSupportedLoader() -#endif - ); - } + AssetLoader.RegisterResUriParsers(); + AvaloniaLocator.CurrentMutable + .Bind().ToSingleton() + .Bind().ToConstant(new StandardAssetLoader(assembly)); } } diff --git a/src/Avalonia.Base/Rendering/DefaultRenderTimer.cs b/src/Avalonia.Base/Rendering/DefaultRenderTimer.cs index 74c8bd107a..102cc30e87 100644 --- a/src/Avalonia.Base/Rendering/DefaultRenderTimer.cs +++ b/src/Avalonia.Base/Rendering/DefaultRenderTimer.cs @@ -1,4 +1,5 @@ using System; +using System.Threading; using Avalonia.Metadata; using Avalonia.Platform; @@ -14,7 +15,6 @@ namespace Avalonia.Rendering [PrivateApi] public class DefaultRenderTimer : IRenderTimer { - private IRuntimePlatform? _runtime; private int _subscriberCount; private Action? _tick; private IDisposable? _subscription; @@ -80,11 +80,9 @@ namespace Avalonia.Rendering /// protected virtual IDisposable StartCore(Action tick) { - _runtime ??= AvaloniaLocator.Current.GetRequiredService(); + var interval = TimeSpan.FromSeconds(1.0 / FramesPerSecond); - return _runtime.StartSystemTimer( - TimeSpan.FromSeconds(1.0 / FramesPerSecond), - () => tick(TimeSpan.FromMilliseconds(Environment.TickCount))); + return new Timer(_ => tick(TimeSpan.FromMilliseconds(Environment.TickCount)), null, interval, interval); } /// diff --git a/src/Avalonia.Controls/AppBuilder.cs b/src/Avalonia.Controls/AppBuilder.cs index 1cac22c8ce..436e618a6b 100644 --- a/src/Avalonia.Controls/AppBuilder.cs +++ b/src/Avalonia.Controls/AppBuilder.cs @@ -12,23 +12,23 @@ namespace Avalonia /// /// Initializes platform-specific services for an . /// - public class AppBuilder + public sealed class AppBuilder { private static bool s_setupWasAlreadyCalled; private Action? _optionsInitializers; private Func? _appFactory; private IApplicationLifetime? _lifetime; - - /// - /// Gets or sets the instance. - /// - public IRuntimePlatform RuntimePlatform { get; set; } /// /// Gets or sets a method to call the initialize the runtime platform services (e. g. AssetLoader) /// - public Action RuntimePlatformServicesInitializer { get; private set; } + public Action? RuntimePlatformServicesInitializer { get; private set; } + /// + /// Gets the name of the currently selected windowing subsystem. + /// + public string? RuntimePlatformServicesName { get; private set; } + /// /// Gets the instance being initialized. /// @@ -70,21 +70,10 @@ namespace Avalonia /// /// Initializes a new instance of the class. /// - public AppBuilder() - : this(new StandardRuntimePlatform(), - builder => StandardRuntimePlatformServices.Register(builder.ApplicationType?.Assembly)) + private AppBuilder() { } - /// - /// Initializes a new instance of the class. - /// - protected AppBuilder(IRuntimePlatform platform, Action platformServices) - { - RuntimePlatform = platform; - RuntimePlatformServicesInitializer = () => platformServices(this); - } - /// /// Begin configuring an . /// @@ -96,7 +85,6 @@ namespace Avalonia return new AppBuilder() { ApplicationType = typeof(TApp), - // Needed for CoreRT compatibility _appFactory = () => new TApp() }; } @@ -155,7 +143,7 @@ namespace Avalonia $"Input type either needs to have BuildAvaloniaApp -> AppBuilder method or inherit Application type."); } - protected AppBuilder Self => this; + private AppBuilder Self => this; public AppBuilder AfterSetup(Action callback) { @@ -225,6 +213,30 @@ namespace Avalonia RenderingSubsystemName = name; return Self; } + + /// + /// Specifies a runtime platform subsystem to use. + /// + /// The method to call to initialize the runtime platform subsystem. + /// The name of the runtime platform subsystem. + /// An instance. + public AppBuilder UseRuntimePlatformSubsystem(Action initializer, string name = "") + { + RuntimePlatformServicesInitializer = initializer; + RuntimePlatformServicesName = name; + return Self; + } + + /// + /// Specifies a standard runtime platform subsystem to use. + /// + /// An instance. + public AppBuilder UseStandardRuntimePlatformSubsystem() + { + RuntimePlatformServicesInitializer = () => StandardRuntimePlatformServices.Register(ApplicationType?.Assembly); + RuntimePlatformServicesName = nameof(StandardRuntimePlatform); + return Self; + } /// /// Configures platform-specific options diff --git a/src/Avalonia.DesignerSupport/Remote/RemoteDesignerEntryPoint.cs b/src/Avalonia.DesignerSupport/Remote/RemoteDesignerEntryPoint.cs index 6a6bc8c746..06d63a65ba 100644 --- a/src/Avalonia.DesignerSupport/Remote/RemoteDesignerEntryPoint.cs +++ b/src/Avalonia.DesignerSupport/Remote/RemoteDesignerEntryPoint.cs @@ -138,6 +138,7 @@ namespace Avalonia.DesignerSupport.Remote CommandLineArgs args, object obj) { var builder = (AppBuilder)obj; + builder = builder.UseStandardRuntimePlatformSubsystem(); if (args.Method == Methods.AvaloniaRemote) builder.UseWindowingSubsystem(() => PreviewerWindowingPlatform.Initialize(transport)); if (args.Method == Methods.Html) diff --git a/src/Avalonia.Native/AvaloniaNativePlatformExtensions.cs b/src/Avalonia.Native/AvaloniaNativePlatformExtensions.cs index b670f302ac..1d271c9d75 100644 --- a/src/Avalonia.Native/AvaloniaNativePlatformExtensions.cs +++ b/src/Avalonia.Native/AvaloniaNativePlatformExtensions.cs @@ -9,7 +9,9 @@ namespace Avalonia { public static AppBuilder UseAvaloniaNative(this AppBuilder builder) { - builder.UseWindowingSubsystem(() => + builder + .UseStandardRuntimePlatformSubsystem() + .UseWindowingSubsystem(() => { var platform = AvaloniaNativePlatform.Initialize( AvaloniaLocator.Current.GetService() ?? diff --git a/src/Avalonia.OpenGL/Egl/EglInterface.cs b/src/Avalonia.OpenGL/Egl/EglInterface.cs index 2235d9087c..4b766d0dda 100644 --- a/src/Avalonia.OpenGL/Egl/EglInterface.cs +++ b/src/Avalonia.OpenGL/Egl/EglInterface.cs @@ -35,9 +35,8 @@ namespace Avalonia.OpenGL.Egl static Func Load(string library) { - var dyn = AvaloniaLocator.Current.GetRequiredService(); - var lib = dyn.LoadLibrary(library); - return (s) => dyn.GetProcAddress(lib, s, true); + var lib = NativeLibraryEx.Load(library); + return (s) => NativeLibraryEx.TryGetExport(lib, s, out var address) ? address : default; } // ReSharper disable UnassignedGetOnlyAutoProperty diff --git a/src/Avalonia.X11/X11CursorFactory.cs b/src/Avalonia.X11/X11CursorFactory.cs index 4060869b19..9db7694604 100644 --- a/src/Avalonia.X11/X11CursorFactory.cs +++ b/src/Avalonia.X11/X11CursorFactory.cs @@ -5,6 +5,7 @@ using System.Runtime.InteropServices; using Avalonia.Controls.Platform.Surfaces; using Avalonia.Input; using Avalonia.Platform; +using Avalonia.Platform.Internal; using Avalonia.SourceGenerator; using Avalonia.Utilities; @@ -92,17 +93,16 @@ namespace Avalonia.X11 private unsafe class XImageCursor : CursorImpl, IFramebufferPlatformSurface, IPlatformHandle { private readonly PixelSize _pixelSize; - private readonly IUnmanagedBlob _blob; + private readonly UnmanagedBlob _blob; public XImageCursor(IntPtr display, IBitmapImpl bitmap, PixelPoint hotSpot) { var size = Marshal.SizeOf() + (bitmap.PixelSize.Width * bitmap.PixelSize.Height * 4); - var runtimePlatform = AvaloniaLocator.Current.GetRequiredService(); var platformRenderInterface = AvaloniaLocator.Current.GetRequiredService(); _pixelSize = bitmap.PixelSize; - _blob = runtimePlatform.AllocBlob(size); + _blob = new UnmanagedBlob(size); var image = (XcursorImage*)_blob.Address; image->version = 1; diff --git a/src/Avalonia.X11/X11Framebuffer.cs b/src/Avalonia.X11/X11Framebuffer.cs index 90e4b18571..02cfe4a517 100644 --- a/src/Avalonia.X11/X11Framebuffer.cs +++ b/src/Avalonia.X11/X11Framebuffer.cs @@ -1,6 +1,7 @@ using System; using System.IO; using Avalonia.Platform; +using Avalonia.Platform.Internal; using SkiaSharp; using static Avalonia.X11.XLib; namespace Avalonia.X11 @@ -10,7 +11,7 @@ namespace Avalonia.X11 private readonly IntPtr _display; private readonly IntPtr _xid; private readonly int _depth; - private IUnmanagedBlob _blob; + private UnmanagedBlob _blob; public X11Framebuffer(IntPtr display, IntPtr xid, int depth, int width, int height, double factor) { @@ -25,7 +26,7 @@ namespace Avalonia.X11 RowBytes = width * 4; Dpi = new Vector(96, 96) * factor; Format = PixelFormat.Bgra8888; - _blob = AvaloniaLocator.Current.GetRequiredService().AllocBlob(RowBytes * height); + _blob = new UnmanagedBlob(RowBytes * height); Address = _blob.Address; } diff --git a/src/Avalonia.X11/X11Platform.cs b/src/Avalonia.X11/X11Platform.cs index c70c17d523..12e4e09158 100644 --- a/src/Avalonia.X11/X11Platform.cs +++ b/src/Avalonia.X11/X11Platform.cs @@ -344,7 +344,9 @@ namespace Avalonia { public static AppBuilder UseX11(this AppBuilder builder) { - builder.UseWindowingSubsystem(() => + builder + .UseStandardRuntimePlatformSubsystem() + .UseWindowingSubsystem(() => new AvaloniaX11Platform().Initialize(AvaloniaLocator.Current.GetService() ?? new X11PlatformOptions())); return builder; diff --git a/src/Browser/Avalonia.Browser/BrowserAppBuilder.cs b/src/Browser/Avalonia.Browser/BrowserAppBuilder.cs index 38784871b1..dbc5f9f4a9 100644 --- a/src/Browser/Avalonia.Browser/BrowserAppBuilder.cs +++ b/src/Browser/Avalonia.Browser/BrowserAppBuilder.cs @@ -98,6 +98,7 @@ public static class BrowserAppBuilder this AppBuilder builder) { return builder + .UseBrowserRuntimePlatformSubsystem() .UseWindowingSubsystem(BrowserWindowingPlatform.Register) .UseSkia(); } diff --git a/src/Browser/Avalonia.Browser/BrowserRuntimePlatform.cs b/src/Browser/Avalonia.Browser/BrowserRuntimePlatform.cs index 67bb040410..eee2b70c9c 100644 --- a/src/Browser/Avalonia.Browser/BrowserRuntimePlatform.cs +++ b/src/Browser/Avalonia.Browser/BrowserRuntimePlatform.cs @@ -1,4 +1,5 @@ using System; +using System.Reflection; using System.Runtime.InteropServices; using System.Runtime.InteropServices.JavaScript; using System.Text.RegularExpressions; @@ -7,6 +8,23 @@ using Avalonia.Platform; namespace Avalonia.Browser; +internal static class BrowserRuntimePlatformServices +{ + public static AppBuilder UseBrowserRuntimePlatformSubsystem(this AppBuilder builder) + { + builder.UseRuntimePlatformSubsystem(() => Register(builder.ApplicationType?.Assembly), nameof(BrowserRuntimePlatform)); + return builder; + } + + public static void Register(Assembly? assembly = null) + { + AssetLoader.RegisterResUriParsers(); + AvaloniaLocator.CurrentMutable + .Bind().ToSingleton() + .Bind().ToConstant(new StandardAssetLoader(assembly)); + } +} + internal class BrowserRuntimePlatform : StandardRuntimePlatform { private static readonly Lazy Info = new(() => diff --git a/src/Browser/Avalonia.Browser/WindowingPlatform.cs b/src/Browser/Avalonia.Browser/WindowingPlatform.cs index 3ad2620206..a23cd01910 100644 --- a/src/Browser/Avalonia.Browser/WindowingPlatform.cs +++ b/src/Browser/Avalonia.Browser/WindowingPlatform.cs @@ -57,12 +57,11 @@ namespace Avalonia.Browser public IDisposable StartTimer(DispatcherPriority priority, TimeSpan interval, Action tick) { - return GetRuntimePlatform() - .StartSystemTimer(interval, () => - { - Dispatcher.UIThread.RunJobs(priority); - tick(); - }); + return new Timer(_ => + { + Dispatcher.UIThread.RunJobs(priority); + tick(); + }, null, interval, interval); } public void Signal(DispatcherPriority priority) @@ -71,18 +70,16 @@ namespace Avalonia.Browser return; _signaled = true; + var interval = TimeSpan.FromMilliseconds(1); IDisposable? disp = null; + disp = new Timer(_ => + { + _signaled = false; + disp?.Dispose(); - disp = GetRuntimePlatform() - .StartSystemTimer(TimeSpan.FromMilliseconds(1), - () => - { - _signaled = false; - disp?.Dispose(); - - Signaled?.Invoke(null); - }); + Signaled?.Invoke(null); + }, null, interval, interval); } public bool CurrentThreadIsLoopThread @@ -94,10 +91,5 @@ namespace Avalonia.Browser } public event Action? Signaled; - - private static IRuntimePlatform GetRuntimePlatform() - { - return AvaloniaLocator.Current.GetRequiredService(); - } } } diff --git a/src/Headless/Avalonia.Headless/AvaloniaHeadlessPlatform.cs b/src/Headless/Avalonia.Headless/AvaloniaHeadlessPlatform.cs index 2d8506f92b..9bb587c9b1 100644 --- a/src/Headless/Avalonia.Headless/AvaloniaHeadlessPlatform.cs +++ b/src/Headless/Avalonia.Headless/AvaloniaHeadlessPlatform.cs @@ -105,8 +105,10 @@ namespace Avalonia.Headless public static AppBuilder UseHeadless(this AppBuilder builder, AvaloniaHeadlessPlatformOptions opts) { if(opts.UseHeadlessDrawing) - builder.UseRenderingSubsystem(HeadlessPlatformRenderInterface.Initialize, "Headless"); - return builder.UseWindowingSubsystem(() => AvaloniaHeadlessPlatform.Initialize(opts), "Headless"); + builder = builder.UseRenderingSubsystem(HeadlessPlatformRenderInterface.Initialize, "Headless"); + return builder + .UseStandardRuntimePlatformSubsystem() + .UseWindowingSubsystem(() => AvaloniaHeadlessPlatform.Initialize(opts), "Headless"); } } } diff --git a/src/Linux/Avalonia.LinuxFramebuffer/LinuxFramebufferPlatform.cs b/src/Linux/Avalonia.LinuxFramebuffer/LinuxFramebufferPlatform.cs index 10ec4372c7..e4a48c78fe 100644 --- a/src/Linux/Avalonia.LinuxFramebuffer/LinuxFramebufferPlatform.cs +++ b/src/Linux/Avalonia.LinuxFramebuffer/LinuxFramebufferPlatform.cs @@ -76,7 +76,10 @@ namespace Avalonia.LinuxFramebuffer internal static LinuxFramebufferLifetime Initialize(AppBuilder builder, IOutputBackend outputBackend, IInputBackend? inputBackend) { var platform = new LinuxFramebufferPlatform(outputBackend); - builder.UseSkia().UseWindowingSubsystem(platform.Initialize, "fbdev"); + builder + .UseStandardRuntimePlatformSubsystem() + .UseSkia() + .UseWindowingSubsystem(platform.Initialize, "fbdev"); return new LinuxFramebufferLifetime(platform._fb, inputBackend); } } diff --git a/src/Skia/Avalonia.Skia/Gpu/Metal/SkiaMetalApi.cs b/src/Skia/Avalonia.Skia/Gpu/Metal/SkiaMetalApi.cs index 9475d96fc0..3022f92ec5 100644 --- a/src/Skia/Avalonia.Skia/Gpu/Metal/SkiaMetalApi.cs +++ b/src/Skia/Avalonia.Skia/Gpu/Metal/SkiaMetalApi.cs @@ -2,6 +2,7 @@ using System; using System.Diagnostics.CodeAnalysis; using System.Reflection; using System.Runtime.InteropServices; +using Avalonia.Compatibility; using Avalonia.Platform.Interop; using SkiaSharp; using BindingFlags = System.Reflection.BindingFlags; @@ -24,19 +25,33 @@ internal unsafe class SkiaMetalApi { // Make sure that skia is loaded GC.KeepAlive(new SKPaint()); - - var loader = AvaloniaLocator.Current.GetRequiredService(); -#if NET6_0_OR_GREATER - var dll = NativeLibrary.Load("libSkiaSharp", typeof(SKPaint).Assembly, null); -#else - var dll = loader.LoadLibrary("libSkiaSharp"); -#endif - _gr_direct_context_make_metal_with_options = (delegate* unmanaged[Stdcall] ) - loader.GetProcAddress(dll, "gr_direct_context_make_metal_with_options", false); - _gr_backendrendertarget_new_metal = - (delegate* unmanaged[Stdcall]) - loader.GetProcAddress(dll, "gr_backendrendertarget_new_metal", false); - + + var dll = NativeLibraryEx.Load("libSkiaSharp", typeof(SKPaint).Assembly); + + IntPtr address; + + if (NativeLibraryEx.TryGetExport(dll, "gr_direct_context_make_metal_with_options", out address)) + { + _gr_direct_context_make_metal_with_options = + (delegate* unmanaged[Stdcall] )address; + } + else + { + throw new InvalidOperationException( + "Unable to export gr_direct_context_make_metal_with_options. Make sure SkiaSharp is up to date."); + } + + if(NativeLibraryEx.TryGetExport(dll, "gr_backendrendertarget_new_metal", out address)) + { + _gr_backendrendertarget_new_metal = + (delegate* unmanaged[Stdcall])address; + } + else + { + throw new InvalidOperationException( + "Unable to export gr_backendrendertarget_new_metal. Make sure SkiaSharp is up to date."); + } + _contextCtor = typeof(GRContext).GetConstructor( BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, new[] { typeof(IntPtr), typeof(bool) }, null) ?? throw new MissingMemberException("GRContext.ctor(IntPtr,bool)"); diff --git a/src/Skia/Avalonia.Skia/WriteableBitmapImpl.cs b/src/Skia/Avalonia.Skia/WriteableBitmapImpl.cs index 38df7b2933..581470fcfe 100644 --- a/src/Skia/Avalonia.Skia/WriteableBitmapImpl.cs +++ b/src/Skia/Avalonia.Skia/WriteableBitmapImpl.cs @@ -3,6 +3,7 @@ using System.IO; using System.Threading; using Avalonia.Media.Imaging; using Avalonia.Platform; +using Avalonia.Platform.Internal; using Avalonia.Skia.Helpers; using SkiaSharp; @@ -97,21 +98,12 @@ namespace Avalonia.Skia SKColorType colorType = format.ToSkColorType(); SKAlphaType alphaType = alphaFormat.ToSkAlphaType(); - var runtimePlatform = AvaloniaLocator.Current.GetService(); + _bitmap = new SKBitmap(); - if (runtimePlatform != null) - { - _bitmap = new SKBitmap(); - - var nfo = new SKImageInfo(size.Width, size.Height, colorType, alphaType); - var blob = runtimePlatform.AllocBlob(nfo.BytesSize); + var nfo = new SKImageInfo(size.Width, size.Height, colorType, alphaType); + var blob = new UnmanagedBlob(nfo.BytesSize); - _bitmap.InstallPixels(nfo, blob.Address, nfo.RowBytes, s_releaseDelegate, blob); - } - else - { - _bitmap = new SKBitmap(size.Width, size.Height, colorType, alphaType); - } + _bitmap.InstallPixels(nfo, blob.Address, nfo.RowBytes, s_releaseDelegate, blob); _bitmap.Erase(SKColor.Empty); } @@ -176,7 +168,7 @@ namespace Avalonia.Skia /// Blob. private static void ReleaseProc(IntPtr address, object ctx) { - ((IUnmanagedBlob)ctx).Dispose(); + ((UnmanagedBlob)ctx).Dispose(); } /// diff --git a/src/Windows/Avalonia.Win32/FramebufferManager.cs b/src/Windows/Avalonia.Win32/FramebufferManager.cs index 47cab24aa2..ddf90e70e6 100644 --- a/src/Windows/Avalonia.Win32/FramebufferManager.cs +++ b/src/Windows/Avalonia.Win32/FramebufferManager.cs @@ -2,6 +2,7 @@ using System.Threading; using Avalonia.Controls.Platform.Surfaces; using Avalonia.Platform; +using Avalonia.Platform.Internal; using Avalonia.Win32.Interop; namespace Avalonia.Win32 @@ -107,8 +108,7 @@ namespace Avalonia.Win32 private static FramebufferData AllocateFramebufferData(int width, int height) { - var service = AvaloniaLocator.Current.GetRequiredService(); - var bitmapBlob = service.AllocBlob(width * height * _bytesPerPixel); + var bitmapBlob = new UnmanagedBlob(width * height * _bytesPerPixel); return new FramebufferData(bitmapBlob, width, height); } @@ -157,7 +157,7 @@ namespace Avalonia.Win32 private readonly struct FramebufferData { - public IUnmanagedBlob Data { get; } + public UnmanagedBlob Data { get; } public PixelSize Size { get; } @@ -165,7 +165,7 @@ namespace Avalonia.Win32 public UnmanagedMethods.BITMAPINFOHEADER Header { get; } - public FramebufferData(IUnmanagedBlob data, int width, int height) + public FramebufferData(UnmanagedBlob data, int width, int height) { Data = data; Size = new PixelSize(width, height); diff --git a/src/Windows/Avalonia.Win32/Win32Platform.cs b/src/Windows/Avalonia.Win32/Win32Platform.cs index 6e727ba4d4..9244da5064 100644 --- a/src/Windows/Avalonia.Win32/Win32Platform.cs +++ b/src/Windows/Avalonia.Win32/Win32Platform.cs @@ -24,7 +24,9 @@ namespace Avalonia { public static AppBuilder UseWin32(this AppBuilder builder) { - return builder.UseWindowingSubsystem( + return builder + .UseStandardRuntimePlatformSubsystem() + .UseWindowingSubsystem( () => Win32.Win32Platform.Initialize( AvaloniaLocator.Current.GetService() ?? new Win32PlatformOptions()), "Win32"); diff --git a/src/iOS/Avalonia.iOS/Platform.cs b/src/iOS/Avalonia.iOS/Platform.cs index de664c93e0..34018560e2 100644 --- a/src/iOS/Avalonia.iOS/Platform.cs +++ b/src/iOS/Avalonia.iOS/Platform.cs @@ -16,6 +16,7 @@ namespace Avalonia public static AppBuilder UseiOS(this AppBuilder builder) { return builder + .UseStandardRuntimePlatformSubsystem() .UseWindowingSubsystem(iOS.Platform.Register, "iOS") .UseSkia(); } diff --git a/tests/Avalonia.Benchmarks/Styling/ResourceBenchmarks.cs b/tests/Avalonia.Benchmarks/Styling/ResourceBenchmarks.cs index ebd8a6474a..c0ec3f0687 100644 --- a/tests/Avalonia.Benchmarks/Styling/ResourceBenchmarks.cs +++ b/tests/Avalonia.Benchmarks/Styling/ResourceBenchmarks.cs @@ -20,7 +20,7 @@ namespace Avalonia.Benchmarks.Styling var services = new TestServices( assetLoader: new StandardAssetLoader(), globalClock: new MockGlobalClock(), - platform: new AppBuilder().RuntimePlatform, + platform: new StandardRuntimePlatform(), standardCursorFactory: Mock.Of(), theme: () => CreateTheme(), windowingPlatform: new MockWindowingPlatform());