committed by
GitHub
66 changed files with 373 additions and 1161 deletions
@ -1,35 +1,18 @@ |
|||
using System; |
|||
using System.Reactive.Linq; |
|||
using Avalonia; |
|||
using Avalonia.Animation; |
|||
using Avalonia.Controls; |
|||
using Avalonia.Data; |
|||
using Avalonia.Markup.Xaml; |
|||
using Avalonia.Media; |
|||
|
|||
namespace RenderDemo.Pages |
|||
{ |
|||
public class ClippingPage : UserControl |
|||
{ |
|||
private Geometry _clip; |
|||
|
|||
public ClippingPage() |
|||
{ |
|||
InitializeComponent(); |
|||
WireUpCheckbox(); |
|||
} |
|||
|
|||
private void InitializeComponent() |
|||
{ |
|||
AvaloniaXamlLoader.Load(this); |
|||
} |
|||
|
|||
private void WireUpCheckbox() |
|||
{ |
|||
var useMask = this.FindControl<CheckBox>("useMask"); |
|||
var clipped = this.FindControl<Border>("clipped"); |
|||
_clip = clipped.Clip; |
|||
useMask.Click += (s, e) => clipped.Clip = clipped.Clip == null ? _clip : null; |
|||
} |
|||
} |
|||
} |
|||
|
|||
@ -1,18 +0,0 @@ |
|||
using Avalonia.Platform; |
|||
|
|||
namespace Avalonia.Shared.PlatformSupport |
|||
{ |
|||
internal partial class StandardRuntimePlatform |
|||
{ |
|||
public RuntimePlatformInfo GetRuntimeInfo() => new RuntimePlatformInfo |
|||
{ |
|||
IsCoreClr = false, |
|||
IsDesktop = false, |
|||
IsMobile = true, |
|||
IsDotNetFramework = false, |
|||
IsMono = true, |
|||
IsUnix = true, |
|||
OperatingSystem = OperatingSystemType.Android |
|||
}; |
|||
} |
|||
} |
|||
@ -0,0 +1,3 @@ |
|||
Compat issues with assembly Avalonia.DesktopRuntime: |
|||
TypesMustExist : Type 'Avalonia.Shared.PlatformSupport.AssetLoader' does not exist in the implementation but it does exist in the contract. |
|||
Total Issues: 1 |
|||
@ -1,40 +0,0 @@ |
|||
using System; |
|||
using System.Runtime.InteropServices; |
|||
using Avalonia.Platform; |
|||
|
|||
|
|||
namespace Avalonia.Shared.PlatformSupport |
|||
{ |
|||
internal partial class StandardRuntimePlatform |
|||
{ |
|||
private static readonly Lazy<RuntimePlatformInfo> Info = new Lazy<RuntimePlatformInfo>(() => |
|||
{ |
|||
OperatingSystemType os; |
|||
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 |
|||
throw new Exception("Unknown OS platform " + RuntimeInformation.OSDescription); |
|||
|
|||
return new RuntimePlatformInfo |
|||
{ |
|||
#if NETCOREAPP2_0
|
|||
IsCoreClr = true, |
|||
#elif NET461
|
|||
IsDotNetFramework = false, |
|||
#endif
|
|||
IsDesktop = true, |
|||
IsMono = false, |
|||
IsMobile = false, |
|||
IsUnix = os != OperatingSystemType.WinNT, |
|||
OperatingSystem = os, |
|||
}; |
|||
}); |
|||
|
|||
|
|||
public RuntimePlatformInfo GetRuntimeInfo() => Info.Value; |
|||
} |
|||
} |
|||
@ -0,0 +1,18 @@ |
|||
<Project Sdk="Microsoft.NET.Sdk"> |
|||
|
|||
<PropertyGroup> |
|||
<TargetFrameworks>net6.0;net461;netstandard2.0</TargetFrameworks> |
|||
</PropertyGroup> |
|||
|
|||
<ItemGroup> |
|||
<ProjectReference Include="../Avalonia.Base/Avalonia.Base.csproj" /> |
|||
</ItemGroup> |
|||
|
|||
<ItemGroup> |
|||
<PackageReference Include="System.Runtime.InteropServices.RuntimeInformation" Version="4.3.0" Condition="'$(TargetFramework)' == 'net461'" /> |
|||
</ItemGroup> |
|||
|
|||
<Import Project="..\..\build\NetCore.props" /> |
|||
<Import Project="..\..\build\NetFX.props" /> |
|||
<Import Project="..\..\build\NullableEnable.props" /> |
|||
</Project> |
|||
@ -0,0 +1,31 @@ |
|||
using System.Reflection; |
|||
using Avalonia.Platform; |
|||
using Avalonia.Platform.Interop; |
|||
|
|||
namespace Avalonia.PlatformSupport |
|||
{ |
|||
public static class StandardRuntimePlatformServices |
|||
{ |
|||
public static void Register(Assembly? assembly = null) |
|||
{ |
|||
var standardPlatform = new StandardRuntimePlatform(); |
|||
var os = standardPlatform.GetRuntimeInfo().OperatingSystem; |
|||
|
|||
AssetLoader.RegisterResUriParsers(); |
|||
AvaloniaLocator.CurrentMutable |
|||
.Bind<IRuntimePlatform>().ToConstant(standardPlatform) |
|||
.Bind<IAssetLoader>().ToConstant(new AssetLoader(assembly)) |
|||
.Bind<IDynamicLibraryLoader>().ToConstant( |
|||
os switch |
|||
{ |
|||
OperatingSystemType.WinNT => new Win32Loader(), |
|||
OperatingSystemType.OSX => new UnixLoader(), |
|||
OperatingSystemType.Linux => new UnixLoader(), |
|||
OperatingSystemType.Android => new UnixLoader(), |
|||
// iOS, WASM, ...
|
|||
_ => (IDynamicLibraryLoader)new NotSupportedLoader() |
|||
} |
|||
); |
|||
} |
|||
} |
|||
} |
|||
@ -1,17 +0,0 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> |
|||
<PropertyGroup> |
|||
<MSBuildAllProjects>$(MSBuildAllProjects);$(MSBuildThisFileFullPath)</MSBuildAllProjects> |
|||
<HasSharedItems>true</HasSharedItems> |
|||
<SharedGUID>{E4D9629C-F168-4224-8F51-F5E482FFEC42}</SharedGUID> |
|||
</PropertyGroup> |
|||
<PropertyGroup Label="Configuration"> |
|||
<Import_RootNamespace>Avalonia.Shared.PlatformSupport</Import_RootNamespace> |
|||
</PropertyGroup> |
|||
<ItemGroup> |
|||
<Compile Include="$(MSBuildThisFileDirectory)AssetLoader.cs" /> |
|||
<Compile Include="$(MSBuildThisFileDirectory)DynLoader.cs" /> |
|||
<Compile Include="$(MSBuildThisFileDirectory)StandardRuntimePlatform.cs" /> |
|||
<Compile Include="$(MSBuildThisFileDirectory)StandardRuntimePlatformServices.cs" /> |
|||
</ItemGroup> |
|||
</Project> |
|||
@ -1,11 +0,0 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> |
|||
<PropertyGroup> |
|||
<ProjectGuid>{E4D9629C-F168-4224-3F51-A5E482FFBC42}</ProjectGuid> |
|||
</PropertyGroup> |
|||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" /> |
|||
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\CodeSharing\Microsoft.CodeSharing.Common.Default.props" /> |
|||
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\CodeSharing\Microsoft.CodeSharing.Common.props" /> |
|||
<Import Project="PlatformSupport.projitems" Label="Shared" /> |
|||
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\CodeSharing\Microsoft.CodeSharing.CSharp.targets" /> |
|||
</Project> |
|||
@ -1,28 +0,0 @@ |
|||
using System.Reflection; |
|||
using System.Runtime.InteropServices; |
|||
using Avalonia.Platform; |
|||
using Avalonia.Platform.Interop; |
|||
|
|||
namespace Avalonia.Shared.PlatformSupport |
|||
{ |
|||
static class StandardRuntimePlatformServices |
|||
{ |
|||
public static void Register(Assembly assembly = null) |
|||
{ |
|||
var standardPlatform = new StandardRuntimePlatform(); |
|||
AssetLoader.RegisterResUriParsers(); |
|||
AvaloniaLocator.CurrentMutable |
|||
.Bind<IRuntimePlatform>().ToConstant(standardPlatform) |
|||
.Bind<IAssetLoader>().ToConstant(new AssetLoader(assembly)) |
|||
.Bind<IDynamicLibraryLoader>().ToConstant( |
|||
#if __IOS__
|
|||
new IOSLoader() |
|||
#else
|
|||
RuntimeInformation.IsOSPlatform(OSPlatform.Windows) |
|||
? (IDynamicLibraryLoader)new Win32Loader() |
|||
: new UnixLoader() |
|||
#endif
|
|||
); |
|||
} |
|||
} |
|||
} |
|||
@ -1,13 +0,0 @@ |
|||
using Avalonia.Platform; |
|||
|
|||
namespace Avalonia.RenderHelpers |
|||
{ |
|||
static class QuadBezierHelper |
|||
{ |
|||
public static void QuadraticBezierTo(IStreamGeometryContextImpl context, Point current, Point controlPoint, Point endPoint) |
|||
{ |
|||
//(s, (s + 2c)/ 3, (e + 2c)/ 3, e)
|
|||
context.CubicBezierTo((current + 2*controlPoint)/3, (endPoint + 2*controlPoint)/3, endPoint); |
|||
} |
|||
} |
|||
} |
|||
@ -1,14 +0,0 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> |
|||
<PropertyGroup> |
|||
<MSBuildAllProjects>$(MSBuildAllProjects);$(MSBuildThisFileFullPath)</MSBuildAllProjects> |
|||
<HasSharedItems>true</HasSharedItems> |
|||
<SharedGUID>3c4c0cb4-0c0f-4450-a37b-148c84ff905f</SharedGUID> |
|||
</PropertyGroup> |
|||
<PropertyGroup Label="Configuration"> |
|||
<Import_RootNamespace>Avalonia.RenderHelpers</Import_RootNamespace> |
|||
</PropertyGroup> |
|||
<ItemGroup> |
|||
<Compile Include="$(MSBuildThisFileDirectory)QuadBezierHelper.cs" /> |
|||
</ItemGroup> |
|||
</Project> |
|||
@ -1,13 +0,0 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> |
|||
<PropertyGroup Label="Globals"> |
|||
<ProjectGuid>3c4c0cb4-0c0f-4450-a37b-148c84ff905f</ProjectGuid> |
|||
<MinimumVisualStudioVersion>14.0</MinimumVisualStudioVersion> |
|||
</PropertyGroup> |
|||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" /> |
|||
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\CodeSharing\Microsoft.CodeSharing.Common.Default.props" /> |
|||
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\CodeSharing\Microsoft.CodeSharing.Common.props" /> |
|||
<PropertyGroup /> |
|||
<Import Project="RenderHelpers.projitems" Label="Shared" /> |
|||
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\CodeSharing\Microsoft.CodeSharing.CSharp.targets" /> |
|||
</Project> |
|||
@ -1,84 +0,0 @@ |
|||
using System; |
|||
using Avalonia.Controls; |
|||
using Avalonia.Input.Raw; |
|||
using Avalonia.Platform; |
|||
|
|||
namespace Avalonia |
|||
{ |
|||
internal class ManagedWindowResizeDragHelper |
|||
{ |
|||
private readonly IWindowBaseImpl _window; |
|||
private readonly Action<bool> _captureMouse; |
|||
private readonly Action<Rect> _resize; |
|||
private WindowEdge? _edge; |
|||
private Point _prevPoint; |
|||
|
|||
public ManagedWindowResizeDragHelper(IWindowBaseImpl window, Action<bool> captureMouse, Action<Rect> resize = null) |
|||
{ |
|||
_window = window; |
|||
_captureMouse = captureMouse; |
|||
_resize = resize; |
|||
} |
|||
|
|||
public void BeginResizeDrag(WindowEdge edge, Point currentMousePosition) |
|||
{ |
|||
_captureMouse(true); |
|||
_prevPoint = currentMousePosition; |
|||
_edge = edge; |
|||
} |
|||
|
|||
public bool PreprocessInputEvent(ref RawInputEventArgs e) |
|||
{ |
|||
if (_edge == null) |
|||
return false; |
|||
if (e is RawMouseEventArgs args) |
|||
{ |
|||
if (args.Type == RawMouseEventType.LeftButtonUp) |
|||
{ |
|||
_edge = null; |
|||
_captureMouse(false); |
|||
} |
|||
if (args.Type == RawMouseEventType.Move) |
|||
{ |
|||
MoveWindow(args.Position); |
|||
return true; |
|||
} |
|||
|
|||
|
|||
_edge = null; |
|||
} |
|||
|
|||
return false; |
|||
} |
|||
|
|||
private void MoveWindow(Point position) |
|||
{ |
|||
var diff = position - _prevPoint; |
|||
var edge = _edge.Value; |
|||
var rc = new Rect(_window.Position, _window.ClientSize); |
|||
if (edge == WindowEdge.East || edge == WindowEdge.NorthEast || edge == WindowEdge.SouthEast) |
|||
{ |
|||
rc = rc.WithWidth(rc.Width + diff.X); |
|||
_prevPoint = _prevPoint.WithX(position.X); |
|||
} |
|||
if (edge == WindowEdge.West || edge == WindowEdge.NorthWest || edge == WindowEdge.SouthWest) |
|||
rc = rc.WithX(rc.X + diff.X).WithWidth(rc.Width - diff.X); |
|||
if (edge == WindowEdge.South || edge == WindowEdge.SouthWest || edge == WindowEdge.SouthEast) |
|||
{ |
|||
rc = rc.WithHeight(rc.Height + diff.Y); |
|||
_prevPoint = _prevPoint.WithY(position.Y); |
|||
} |
|||
if (edge == WindowEdge.North || edge == WindowEdge.NorthWest || edge == WindowEdge.NorthEast) |
|||
rc = rc.WithY(rc.Y + diff.Y).WithHeight(rc.Height - diff.Y); |
|||
if (_resize != null) |
|||
_resize(rc); |
|||
else |
|||
{ |
|||
if (_window.Position != rc.Position) |
|||
_window.Position = rc.Position; |
|||
if (_window.ClientSize != rc.Size) |
|||
_window.Resize(rc.Size); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -1,61 +0,0 @@ |
|||
using System.Runtime.InteropServices; |
|||
using Avalonia.Platform; |
|||
using Avalonia.Shared.PlatformSupport; |
|||
|
|||
namespace Avalonia.Web.Blazor |
|||
{ |
|||
internal class BlazorRuntimePlatform : IRuntimePlatform |
|||
{ |
|||
public static readonly IRuntimePlatform Instance = new BlazorRuntimePlatform(); |
|||
|
|||
public IDisposable StartSystemTimer(TimeSpan interval, Action tick) |
|||
{ |
|||
return new Timer(_ => tick(), null, interval, interval); |
|||
} |
|||
|
|||
public RuntimePlatformInfo GetRuntimeInfo() |
|||
{ |
|||
return new RuntimePlatformInfo |
|||
{ |
|||
IsDesktop = false, |
|||
IsMobile = false, |
|||
IsMono = true, |
|||
IsUnix = false, |
|||
IsCoreClr = false, |
|||
IsDotNetFramework = false |
|||
}; |
|||
} |
|||
|
|||
private class BasicBlob : IUnmanagedBlob |
|||
{ |
|||
public BasicBlob(int size) |
|||
{ |
|||
Address = Marshal.AllocHGlobal(size); |
|||
Size = size; |
|||
} |
|||
public void Dispose() |
|||
{ |
|||
if (Address != IntPtr.Zero) |
|||
Marshal.FreeHGlobal(Address); |
|||
Address = IntPtr.Zero; |
|||
} |
|||
|
|||
public IntPtr Address { get; private set; } |
|||
|
|||
public int Size { get; } |
|||
public bool IsDisposed => Address == IntPtr.Zero; |
|||
} |
|||
|
|||
public IUnmanagedBlob AllocBlob(int size) |
|||
{ |
|||
return new BasicBlob(size); |
|||
} |
|||
|
|||
public static void RegisterServices(AvaloniaBlazorAppBuilder builder) |
|||
{ |
|||
AssetLoader.RegisterResUriParsers(); |
|||
AvaloniaLocator.CurrentMutable.Bind<IRuntimePlatform>().ToConstant(Instance); |
|||
AvaloniaLocator.CurrentMutable.Bind<IAssetLoader>().ToConstant(new AssetLoader()); |
|||
} |
|||
} |
|||
} |
|||
@ -1,19 +0,0 @@ |
|||
using Avalonia.Platform; |
|||
|
|||
namespace Avalonia.Shared.PlatformSupport |
|||
{ |
|||
partial class StandardRuntimePlatform |
|||
{ |
|||
public RuntimePlatformInfo GetRuntimeInfo() |
|||
{ |
|||
return new RuntimePlatformInfo |
|||
{ |
|||
IsDesktop = false, |
|||
IsMobile = true, |
|||
IsMono = true, |
|||
IsUnix = true, |
|||
OperatingSystem = OperatingSystemType.iOS |
|||
}; |
|||
} |
|||
} |
|||
} |
|||
@ -1,595 +0,0 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.IO; |
|||
using System.Linq; |
|||
using System.Reflection; |
|||
using System.Runtime.CompilerServices; |
|||
using System.Runtime.InteropServices; |
|||
using System.Threading; |
|||
using Avalonia.Platform; |
|||
using Avalonia.Platform.Interop; |
|||
using Avalonia.Utilities; |
|||
|
|||
namespace Avalonia.Shared.PlatformSupport |
|||
{ |
|||
static class StandardRuntimePlatformServices |
|||
{ |
|||
public static void Register(Assembly assembly = null) |
|||
{ |
|||
var standardPlatform = new StandardRuntimePlatform(); |
|||
AssetLoader.RegisterResUriParsers(); |
|||
AvaloniaLocator.CurrentMutable |
|||
.Bind<IRuntimePlatform>().ToConstant(standardPlatform) |
|||
.Bind<IAssetLoader>().ToConstant(new AssetLoader(assembly)) |
|||
.Bind<IDynamicLibraryLoader>().ToConstant( |
|||
#if __IOS__
|
|||
new IOSLoader() |
|||
#else
|
|||
RuntimeInformation.IsOSPlatform(OSPlatform.Windows) |
|||
? (IDynamicLibraryLoader)new Win32Loader() |
|||
: new UnixLoader() |
|||
#endif
|
|||
); |
|||
} |
|||
} |
|||
|
|||
|
|||
internal partial class StandardRuntimePlatform : IRuntimePlatform |
|||
{ |
|||
public IDisposable StartSystemTimer(TimeSpan interval, Action tick) |
|||
{ |
|||
return new Timer(_ => tick(), null, interval, interval); |
|||
} |
|||
|
|||
public IUnmanagedBlob AllocBlob(int size) => new UnmanagedBlob(this, size); |
|||
|
|||
class UnmanagedBlob : IUnmanagedBlob |
|||
{ |
|||
private readonly StandardRuntimePlatform _plat; |
|||
private IntPtr _address; |
|||
private readonly object _lock = new object(); |
|||
#if DEBUG
|
|||
private static readonly List<string> Backtraces = new List<string>(); |
|||
private static Thread GCThread; |
|||
private readonly string _backtrace; |
|||
private static readonly object _btlock = new object(); |
|||
|
|||
class GCThreadDetector |
|||
{ |
|||
~GCThreadDetector() |
|||
{ |
|||
GCThread = Thread.CurrentThread; |
|||
} |
|||
} |
|||
|
|||
[MethodImpl(MethodImplOptions.NoInlining)] |
|||
static void Spawn() => new GCThreadDetector(); |
|||
|
|||
static UnmanagedBlob() |
|||
{ |
|||
Spawn(); |
|||
GC.WaitForPendingFinalizers(); |
|||
} |
|||
|
|||
#endif
|
|||
|
|||
public UnmanagedBlob(StandardRuntimePlatform plat, int size) |
|||
{ |
|||
if (size <= 0) |
|||
throw new ArgumentException("Positive number required", nameof(size)); |
|||
_plat = plat; |
|||
_address = plat.Alloc(size); |
|||
GC.AddMemoryPressure(size); |
|||
Size = size; |
|||
#if DEBUG
|
|||
_backtrace = Environment.StackTrace; |
|||
lock (_btlock) |
|||
Backtraces.Add(_backtrace); |
|||
#endif
|
|||
} |
|||
|
|||
void DoDispose() |
|||
{ |
|||
lock (_lock) |
|||
{ |
|||
if (!IsDisposed) |
|||
{ |
|||
#if DEBUG
|
|||
lock (_btlock) |
|||
Backtraces.Remove(_backtrace); |
|||
#endif
|
|||
_plat?.Free(_address, Size); |
|||
GC.RemoveMemoryPressure(Size); |
|||
IsDisposed = true; |
|||
_address = IntPtr.Zero; |
|||
Size = 0; |
|||
} |
|||
} |
|||
} |
|||
|
|||
public void Dispose() |
|||
{ |
|||
#if DEBUG
|
|||
if (Thread.CurrentThread.ManagedThreadId == GCThread?.ManagedThreadId) |
|||
{ |
|||
lock (_lock) |
|||
{ |
|||
if (!IsDisposed) |
|||
{ |
|||
Console.Error.WriteLine("Native blob disposal from finalizer thread\nBacktrace: " |
|||
+ Environment.StackTrace |
|||
+ "\n\nBlob created by " + _backtrace); |
|||
} |
|||
} |
|||
} |
|||
#endif
|
|||
DoDispose(); |
|||
GC.SuppressFinalize(this); |
|||
} |
|||
|
|||
~UnmanagedBlob() |
|||
{ |
|||
#if DEBUG
|
|||
Console.Error.WriteLine("Undisposed native blob created by " + _backtrace); |
|||
#endif
|
|||
DoDispose(); |
|||
} |
|||
|
|||
public IntPtr Address => IsDisposed ? throw new ObjectDisposedException("UnmanagedBlob") : _address; |
|||
public int Size { get; private set; } |
|||
public bool IsDisposed { get; private set; } |
|||
} |
|||
|
|||
|
|||
|
|||
#if NET461 || NETCOREAPP2_0
|
|||
[DllImport("libc", SetLastError = true)] |
|||
private static extern IntPtr mmap(IntPtr addr, IntPtr length, int prot, int flags, int fd, IntPtr offset); |
|||
[DllImport("libc", SetLastError = true)] |
|||
private static extern int munmap(IntPtr addr, IntPtr length); |
|||
[DllImport("libc", SetLastError = true)] |
|||
private static extern long sysconf(int name); |
|||
|
|||
private bool? _useMmap; |
|||
private bool UseMmap |
|||
=> _useMmap ?? ((_useMmap = GetRuntimeInfo().OperatingSystem == OperatingSystemType.Linux)).Value; |
|||
|
|||
IntPtr Alloc(int size) |
|||
{ |
|||
if (UseMmap) |
|||
{ |
|||
var rv = mmap(IntPtr.Zero, new IntPtr(size), 3, 0x22, -1, IntPtr.Zero); |
|||
if (rv.ToInt64() == -1 || (ulong) rv.ToInt64() == 0xffffffff) |
|||
{ |
|||
var errno = Marshal.GetLastWin32Error(); |
|||
throw new Exception("Unable to allocate memory: " + errno); |
|||
} |
|||
return rv; |
|||
} |
|||
else |
|||
return Marshal.AllocHGlobal(size); |
|||
} |
|||
|
|||
void Free(IntPtr ptr, int len) |
|||
{ |
|||
if (UseMmap) |
|||
{ |
|||
if (munmap(ptr, new IntPtr(len)) == -1) |
|||
{ |
|||
var errno = Marshal.GetLastWin32Error(); |
|||
throw new Exception("Unable to free memory: " + errno); |
|||
} |
|||
} |
|||
else |
|||
Marshal.FreeHGlobal(ptr); |
|||
} |
|||
#else
|
|||
IntPtr Alloc(int size) => Marshal.AllocHGlobal(size); |
|||
void Free(IntPtr ptr, int len) => Marshal.FreeHGlobal(ptr); |
|||
#endif
|
|||
|
|||
|
|||
} |
|||
|
|||
internal class IOSLoader : IDynamicLibraryLoader |
|||
{ |
|||
IntPtr IDynamicLibraryLoader.LoadLibrary(string dll) |
|||
{ |
|||
throw new PlatformNotSupportedException(); |
|||
} |
|||
|
|||
IntPtr IDynamicLibraryLoader.GetProcAddress(IntPtr dll, string proc, bool optional) |
|||
{ |
|||
throw new PlatformNotSupportedException(); |
|||
} |
|||
} |
|||
|
|||
public class AssetLoader : IAssetLoader |
|||
{ |
|||
private const string AvaloniaResourceName = "!AvaloniaResources"; |
|||
private static readonly Dictionary<string, AssemblyDescriptor> AssemblyNameCache |
|||
= new Dictionary<string, AssemblyDescriptor>(); |
|||
|
|||
private AssemblyDescriptor _defaultResmAssembly; |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="AssetLoader"/> class.
|
|||
/// </summary>
|
|||
/// <param name="assembly">
|
|||
/// The default assembly from which to load resm: assets for which no assembly is specified.
|
|||
/// </param>
|
|||
public AssetLoader(Assembly assembly = null) |
|||
{ |
|||
if (assembly == null) |
|||
assembly = Assembly.GetEntryAssembly(); |
|||
if (assembly != null) |
|||
_defaultResmAssembly = new AssemblyDescriptor(assembly); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Sets the default assembly from which to load assets for which no assembly is specified.
|
|||
/// </summary>
|
|||
/// <param name="assembly">The default assembly.</param>
|
|||
public void SetDefaultAssembly(Assembly assembly) |
|||
{ |
|||
_defaultResmAssembly = new AssemblyDescriptor(assembly); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Checks if an asset with the specified URI exists.
|
|||
/// </summary>
|
|||
/// <param name="uri">The URI.</param>
|
|||
/// <param name="baseUri">
|
|||
/// A base URI to use if <paramref name="uri"/> is relative.
|
|||
/// </param>
|
|||
/// <returns>True if the asset could be found; otherwise false.</returns>
|
|||
public bool Exists(Uri uri, Uri baseUri = null) |
|||
{ |
|||
return GetAsset(uri, baseUri) != null; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Opens the asset with the requested URI.
|
|||
/// </summary>
|
|||
/// <param name="uri">The URI.</param>
|
|||
/// <param name="baseUri">
|
|||
/// A base URI to use if <paramref name="uri"/> is relative.
|
|||
/// </param>
|
|||
/// <returns>A stream containing the asset contents.</returns>
|
|||
/// <exception cref="FileNotFoundException">
|
|||
/// The asset could not be found.
|
|||
/// </exception>
|
|||
public Stream Open(Uri uri, Uri baseUri = null) => OpenAndGetAssembly(uri, baseUri).Item1; |
|||
|
|||
/// <summary>
|
|||
/// Opens the asset with the requested URI and returns the asset stream and the
|
|||
/// assembly containing the asset.
|
|||
/// </summary>
|
|||
/// <param name="uri">The URI.</param>
|
|||
/// <param name="baseUri">
|
|||
/// A base URI to use if <paramref name="uri"/> is relative.
|
|||
/// </param>
|
|||
/// <returns>
|
|||
/// The stream containing the resource contents together with the assembly.
|
|||
/// </returns>
|
|||
/// <exception cref="FileNotFoundException">
|
|||
/// The asset could not be found.
|
|||
/// </exception>
|
|||
public (Stream stream, Assembly assembly) OpenAndGetAssembly(Uri uri, Uri baseUri = null) |
|||
{ |
|||
var asset = GetAsset(uri, baseUri); |
|||
|
|||
if (asset == null) |
|||
{ |
|||
throw new FileNotFoundException($"The resource {uri} could not be found."); |
|||
} |
|||
|
|||
return (asset.GetStream(), asset.Assembly); |
|||
} |
|||
|
|||
public Assembly GetAssembly(Uri uri, Uri baseUri) |
|||
{ |
|||
if (!uri.IsAbsoluteUri && baseUri != null) |
|||
uri = new Uri(baseUri, uri); |
|||
return GetAssembly(uri).Assembly; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets all assets of a folder and subfolders that match specified uri.
|
|||
/// </summary>
|
|||
/// <param name="uri">The URI.</param>
|
|||
/// <param name="baseUri">Base URI that is used if <paramref name="uri"/> is relative.</param>
|
|||
/// <returns>All matching assets as a tuple of the absolute path to the asset and the assembly containing the asset</returns>
|
|||
public IEnumerable<Uri> GetAssets(Uri uri, Uri baseUri) |
|||
{ |
|||
if (uri.IsAbsoluteUri && uri.Scheme == "resm") |
|||
{ |
|||
var assembly = GetAssembly(uri); |
|||
|
|||
return assembly?.Resources.Where(x => x.Key.Contains(uri.AbsolutePath)) |
|||
.Select(x =>new Uri($"resm:{x.Key}?assembly={assembly.Name}")) ?? |
|||
Enumerable.Empty<Uri>(); |
|||
} |
|||
|
|||
uri = EnsureAbsolute(uri, baseUri); |
|||
if (uri.Scheme == "avares") |
|||
{ |
|||
var (asm, path) = GetResAsmAndPath(uri); |
|||
if (asm == null) |
|||
{ |
|||
throw new ArgumentException( |
|||
"No default assembly, entry assembly or explicit assembly specified; " + |
|||
"don't know where to look up for the resource, try specifying assembly explicitly."); |
|||
} |
|||
|
|||
if (asm?.AvaloniaResources == null) |
|||
return Enumerable.Empty<Uri>(); |
|||
path = path.TrimEnd('/') + '/'; |
|||
return asm.AvaloniaResources.Where(r => r.Key.StartsWith(path)) |
|||
.Select(x => new Uri($"avares://{asm.Name}{x.Key}")); |
|||
} |
|||
|
|||
return Enumerable.Empty<Uri>(); |
|||
} |
|||
|
|||
private Uri EnsureAbsolute(Uri uri, Uri baseUri) |
|||
{ |
|||
if (uri.IsAbsoluteUri) |
|||
return uri; |
|||
if(baseUri == null) |
|||
throw new ArgumentException($"Relative uri {uri} without base url"); |
|||
if (!baseUri.IsAbsoluteUri) |
|||
throw new ArgumentException($"Base uri {baseUri} is relative"); |
|||
if (baseUri.Scheme == "resm") |
|||
throw new ArgumentException( |
|||
$"Relative uris for 'resm' scheme aren't supported; {baseUri} uses resm"); |
|||
return new Uri(baseUri, uri); |
|||
} |
|||
|
|||
private IAssetDescriptor GetAsset(Uri uri, Uri baseUri) |
|||
{ |
|||
if (uri.IsAbsoluteUri && uri.Scheme == "resm") |
|||
{ |
|||
var asm = GetAssembly(uri) ?? GetAssembly(baseUri) ?? _defaultResmAssembly; |
|||
|
|||
if (asm == null) |
|||
{ |
|||
throw new ArgumentException( |
|||
"No default assembly, entry assembly or explicit assembly specified; " + |
|||
"don't know where to look up for the resource, try specifying assembly explicitly."); |
|||
} |
|||
|
|||
IAssetDescriptor rv; |
|||
|
|||
var resourceKey = uri.AbsolutePath; |
|||
asm.Resources.TryGetValue(resourceKey, out rv); |
|||
return rv; |
|||
} |
|||
|
|||
uri = EnsureAbsolute(uri, baseUri); |
|||
|
|||
if (uri.Scheme == "avares") |
|||
{ |
|||
var (asm, path) = GetResAsmAndPath(uri); |
|||
if (asm.AvaloniaResources == null) |
|||
return null; |
|||
asm.AvaloniaResources.TryGetValue(path, out var desc); |
|||
return desc; |
|||
} |
|||
|
|||
throw new ArgumentException($"Unsupported url type: " + uri.Scheme, nameof(uri)); |
|||
} |
|||
|
|||
private (AssemblyDescriptor asm, string path) GetResAsmAndPath(Uri uri) |
|||
{ |
|||
var asm = GetAssembly(uri.Authority); |
|||
return (asm, uri.AbsolutePath); |
|||
} |
|||
|
|||
private AssemblyDescriptor GetAssembly(Uri uri) |
|||
{ |
|||
if (uri != null) |
|||
{ |
|||
if (!uri.IsAbsoluteUri) |
|||
return null; |
|||
if (uri.Scheme == "avares") |
|||
return GetResAsmAndPath(uri).asm; |
|||
|
|||
if (uri.Scheme == "resm") |
|||
{ |
|||
var qs = ParseQueryString(uri); |
|||
string assemblyName; |
|||
|
|||
if (qs.TryGetValue("assembly", out assemblyName)) |
|||
{ |
|||
return GetAssembly(assemblyName); |
|||
} |
|||
} |
|||
} |
|||
|
|||
return null; |
|||
} |
|||
|
|||
private AssemblyDescriptor GetAssembly(string name) |
|||
{ |
|||
if (name == null) |
|||
throw new ArgumentNullException(nameof(name)); |
|||
|
|||
AssemblyDescriptor rv; |
|||
if (!AssemblyNameCache.TryGetValue(name, out rv)) |
|||
{ |
|||
var loadedAssemblies = AppDomain.CurrentDomain.GetAssemblies(); |
|||
var match = loadedAssemblies.FirstOrDefault(a => a.GetName().Name == name); |
|||
if (match != null) |
|||
{ |
|||
AssemblyNameCache[name] = rv = new AssemblyDescriptor(match); |
|||
} |
|||
else |
|||
{ |
|||
// iOS does not support loading assemblies dynamically!
|
|||
//
|
|||
#if __IOS__
|
|||
throw new InvalidOperationException( |
|||
$"Assembly {name} needs to be referenced and explicitly loaded before loading resources"); |
|||
#else
|
|||
name = Uri.UnescapeDataString(name); |
|||
AssemblyNameCache[name] = rv = new AssemblyDescriptor(Assembly.Load(name)); |
|||
#endif
|
|||
} |
|||
} |
|||
|
|||
return rv; |
|||
} |
|||
|
|||
private Dictionary<string, string> ParseQueryString(Uri uri) |
|||
{ |
|||
return uri.Query.TrimStart('?') |
|||
.Split(new[] { '&' }, StringSplitOptions.RemoveEmptyEntries) |
|||
.Select(p => p.Split('=')) |
|||
.ToDictionary(p => p[0], p => p[1]); |
|||
} |
|||
|
|||
private interface IAssetDescriptor |
|||
{ |
|||
Stream GetStream(); |
|||
Assembly Assembly { get; } |
|||
} |
|||
|
|||
private class AssemblyResourceDescriptor : IAssetDescriptor |
|||
{ |
|||
private readonly Assembly _asm; |
|||
private readonly string _name; |
|||
|
|||
public AssemblyResourceDescriptor(Assembly asm, string name) |
|||
{ |
|||
_asm = asm; |
|||
_name = name; |
|||
} |
|||
|
|||
public Stream GetStream() |
|||
{ |
|||
return _asm.GetManifestResourceStream(_name); |
|||
} |
|||
|
|||
public Assembly Assembly => _asm; |
|||
} |
|||
|
|||
private class AvaloniaResourceDescriptor : IAssetDescriptor |
|||
{ |
|||
private readonly int _offset; |
|||
private readonly int _length; |
|||
public Assembly Assembly { get; } |
|||
|
|||
public AvaloniaResourceDescriptor(Assembly asm, int offset, int length) |
|||
{ |
|||
_offset = offset; |
|||
_length = length; |
|||
Assembly = asm; |
|||
} |
|||
|
|||
public Stream GetStream() |
|||
{ |
|||
return new SlicedStream(Assembly.GetManifestResourceStream(AvaloniaResourceName), _offset, _length); |
|||
} |
|||
} |
|||
|
|||
class SlicedStream : Stream |
|||
{ |
|||
private readonly Stream _baseStream; |
|||
private readonly int _from; |
|||
|
|||
public SlicedStream(Stream baseStream, int from, int length) |
|||
{ |
|||
Length = length; |
|||
_baseStream = baseStream; |
|||
_from = from; |
|||
_baseStream.Position = from; |
|||
} |
|||
public override void Flush() |
|||
{ |
|||
} |
|||
|
|||
public override int Read(byte[] buffer, int offset, int count) |
|||
{ |
|||
return _baseStream.Read(buffer, offset, (int)Math.Min(count, Length - Position)); |
|||
} |
|||
|
|||
public override long Seek(long offset, SeekOrigin origin) |
|||
{ |
|||
if (origin == SeekOrigin.Begin) |
|||
Position = offset; |
|||
if (origin == SeekOrigin.End) |
|||
Position = _from + Length + offset; |
|||
if (origin == SeekOrigin.Current) |
|||
Position = Position + offset; |
|||
return Position; |
|||
} |
|||
|
|||
public override void SetLength(long value) => throw new NotSupportedException(); |
|||
|
|||
public override void Write(byte[] buffer, int offset, int count) => throw new NotSupportedException(); |
|||
|
|||
public override bool CanRead => true; |
|||
public override bool CanSeek => _baseStream.CanRead; |
|||
public override bool CanWrite => false; |
|||
public override long Length { get; } |
|||
public override long Position |
|||
{ |
|||
get => _baseStream.Position - _from; |
|||
set => _baseStream.Position = value + _from; |
|||
} |
|||
|
|||
protected override void Dispose(bool disposing) |
|||
{ |
|||
if (disposing) |
|||
_baseStream.Dispose(); |
|||
} |
|||
|
|||
public override void Close() => _baseStream.Close(); |
|||
} |
|||
|
|||
private class AssemblyDescriptor |
|||
{ |
|||
public AssemblyDescriptor(Assembly assembly) |
|||
{ |
|||
Assembly = assembly; |
|||
|
|||
if (assembly != null) |
|||
{ |
|||
Resources = assembly.GetManifestResourceNames() |
|||
.ToDictionary(n => n, n => (IAssetDescriptor)new AssemblyResourceDescriptor(assembly, n)); |
|||
Name = assembly.GetName().Name; |
|||
using (var resources = assembly.GetManifestResourceStream(AvaloniaResourceName)) |
|||
{ |
|||
if (resources != null) |
|||
{ |
|||
Resources.Remove(AvaloniaResourceName); |
|||
|
|||
var indexLength = new BinaryReader(resources).ReadInt32(); |
|||
var index = AvaloniaResourcesIndexReaderWriter.Read(new SlicedStream(resources, 4, indexLength)); |
|||
var baseOffset = indexLength + 4; |
|||
AvaloniaResources = index.ToDictionary(r => "/" + r.Path.TrimStart('/'), r => (IAssetDescriptor) |
|||
new AvaloniaResourceDescriptor(assembly, baseOffset + r.Offset, r.Size)); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
public Assembly Assembly { get; } |
|||
public Dictionary<string, IAssetDescriptor> Resources { get; } |
|||
public Dictionary<string, IAssetDescriptor> AvaloniaResources { get; } |
|||
public string Name { get; } |
|||
} |
|||
|
|||
public static void RegisterResUriParsers() |
|||
{ |
|||
if (!UriParser.IsKnownScheme("avares")) |
|||
UriParser.Register(new GenericUriParser( |
|||
GenericUriParserOptions.GenericAuthority | |
|||
GenericUriParserOptions.NoUserInfo | |
|||
GenericUriParserOptions.NoPort | |
|||
GenericUriParserOptions.NoQuery | |
|||
GenericUriParserOptions.NoFragment), "avares", -1); |
|||
} |
|||
} |
|||
} |
|||
@ -1,16 +0,0 @@ |
|||
using Avalonia.Platform; |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Reflection; |
|||
using System.Text; |
|||
|
|||
namespace Avalonia.Shared.PlatformSupport |
|||
{ |
|||
internal partial class StandardRuntimePlatform : IRuntimePlatform |
|||
{ |
|||
public RuntimePlatformInfo GetRuntimeInfo() |
|||
{ |
|||
return new RuntimePlatformInfo(); |
|||
} |
|||
} |
|||
} |
|||
Loading…
Reference in new issue