committed by
GitHub
38 changed files with 212 additions and 1033 deletions
@ -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