A cross-platform UI framework for .NET
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

235 lines
8.5 KiB

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
//Library loaders were taken from https://github.com/kekekeks/evhttp-sharp/tree/master/EvHttpSharp/Interop (MIT licensed)
namespace Perspex.Skia
{
class MethodTableImpl : MethodTable
{
public MethodTableImpl() : base(GetMethodTable())
{
}
class DetectedPlatformInfo
{
public DetectedPlatformInfo(IDynLoader loader, string name, string cpuArch, string dllExtension)
{
Loader = loader;
Name = name;
CpuArch = cpuArch;
DllExtension = dllExtension;
}
public IDynLoader Loader { get; }
public string Name { get; }
public string CpuArch { get;}
public string DllExtension { get; }
}
static DetectedPlatformInfo DetectPlatform()
{
var macInfo = new DetectedPlatformInfo(new OsxLoader(), "Darwin", IntPtr.Size == 4 ? "i686" : "x86_64", ".dylib");
switch (Environment.OSVersion.Platform)
{
case PlatformID.Unix:
// Well, there are chances MacOSX is reported as Unix instead of MacOSX.
// Instead of platform check, we'll do a feature checks (Mac specific root folders)
if (Directory.Exists("/Applications")
& Directory.Exists("/System")
& Directory.Exists("/Users")
& Directory.Exists("/Volumes"))
return macInfo;
else
{
string cpuArch;
using (var proc =
Process.Start(
(new ProcessStartInfo("uname", "-p")
{
UseShellExecute = false,
RedirectStandardOutput = true
})))
{
proc.WaitForExit();
cpuArch = proc.StandardOutput.ReadToEnd().Trim();
}
return new DetectedPlatformInfo(new LinuxLoader(), "Linux", cpuArch, ".so");
}
case PlatformID.MacOSX:
return macInfo;
default:
return new DetectedPlatformInfo(new Win32Loader(), "Windows", IntPtr.Size == 4 ? "i686" : "x86_64", ".dll");
}
}
static IEnumerable<string> GeneratePossiblePaths()
{
foreach (
var basePath in
new[] {Assembly.GetEntryAssembly(), typeof (MethodTable).Assembly}.Select(
a => Path.GetDirectoryName(a?.GetModules()?[0]?.FullyQualifiedName))
.Concat(new[] { Directory.GetCurrentDirectory()}))
{
if(basePath == null)
continue;
yield return Path.Combine(basePath);
yield return Path.Combine(basePath, "native");
yield return Path.Combine(basePath, "lib");
yield return Path.Combine(basePath, "lib", "native");
}
}
static IntPtr GetMethodTable()
{
var plat = DetectPlatform();
var name = "libperspesk" + plat.DllExtension;
var paths = GeneratePossiblePaths().Select(p => Path.Combine(p, plat.Name, plat.CpuArch, name)).ToList();
var dll = paths.FirstOrDefault(File.Exists);
var msg = "Unable to find native library in following paths: '" +
string.Join("', '", paths) + "'";
Console.Error.WriteLine(msg);
if (dll == null)
throw new FileNotFoundException(msg);
IntPtr hLib = plat.Loader.LoadLibrary(dll);
IntPtr pGetMethodTable = plat.Loader.GetProcAddress(hLib, "GetPerspexMethodTable");
var getTable = (GetPerspexMethodTableDelegate)
Marshal.GetDelegateForFunctionPointer(pGetMethodTable, typeof (GetPerspexMethodTableDelegate));
return getTable();
}
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
delegate IntPtr GetPerspexMethodTableDelegate();
internal interface IDynLoader
{
IntPtr LoadLibrary(string dll);
IntPtr GetProcAddress(IntPtr dll, string proc);
}
class LinuxLoader : IDynLoader
{
// ReSharper disable InconsistentNaming
[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();
// ReSharper restore InconsistentNaming
static string DlError()
{
return Marshal.PtrToStringAuto(dlerror());
}
public IntPtr LoadLibrary(string dll)
{
var handle = dlopen(dll, 1);
if (handle == IntPtr.Zero)
throw new Win32Exception(DlError());
return handle;
}
public IntPtr GetProcAddress(IntPtr dll, string proc)
{
var ptr = dlsym(dll, proc);
if (ptr == IntPtr.Zero)
throw new Win32Exception(DlError());
return ptr;
}
}
internal class Win32Loader : IDynLoader
{
[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);
[DllImport("kernel32", EntryPoint = "SetDllDirectoryW", SetLastError = true, CharSet = CharSet.Unicode)]
extern static bool SetDllDirectory(string lpPathName);
[DllImport("kernel32", EntryPoint = "GetDllDirectoryW", SetLastError = true, CharSet = CharSet.Unicode)]
extern static int GetDllDirectory(int nBufferLength, char[] lpBuffer);
IntPtr IDynLoader.LoadLibrary(string dll)
{
var buffer = new char[2048];
int oldLen = GetDllDirectory(buffer.Length, buffer);
var oldDir = new string(buffer, 0, oldLen);
SetDllDirectory(Path.GetDirectoryName(dll));
var handle = LoadLibrary(dll);
if (handle == IntPtr.Zero)
throw new Win32Exception(Marshal.GetLastWin32Error());
SetDllDirectory(oldDir);
return handle;
}
IntPtr IDynLoader.GetProcAddress(IntPtr dll, string proc)
{
var ptr = GetProcAddress(dll, proc);
if (ptr == IntPtr.Zero)
throw new Win32Exception(Marshal.GetLastWin32Error());
return ptr;
}
}
class OsxLoader : IDynLoader
{
// ReSharper disable InconsistentNaming
[DllImport("/usr/lib/libSystem.dylib")]
public static extern IntPtr dlopen(string path, int mode);
[DllImport("/usr/lib/libSystem.dylib")]
private static extern IntPtr dlsym(IntPtr handle, string symbol);
[DllImport("/usr/lib/libSystem.dylib")]
private static extern IntPtr dlerror();
// ReSharper restore InconsistentNaming
static string DlError()
{
return Marshal.PtrToStringAuto(dlerror());
}
public IntPtr LoadLibrary(string dll)
{
var handle = dlopen(dll, 1);
if (handle == IntPtr.Zero)
throw new Win32Exception(DlError());
return handle;
}
public IntPtr GetProcAddress(IntPtr dll, string proc)
{
var ptr = dlsym(dll, proc);
if (ptr == IntPtr.Zero)
throw new Win32Exception(DlError());
return ptr;
}
}
}
}