diff --git a/appveyor.yml b/appveyor.yml index 1b10d99b4a..e704fe60ff 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -12,6 +12,7 @@ install: - if not exist gtk-sharp-2.12.26.msi appveyor DownloadFile http://download.xamarin.com/GTKforWindows/Windows/gtk-sharp-2.12.26.msi - msiexec /i gtk-sharp-2.12.26.msi /qn /norestart - cmd: set PATH=%programfiles(x86)%\GtkSharp\2.12\bin\;%PATH% + - ps: src\Skia\getnatives.ps1 cache: - gtk-sharp-2.12.26.msi diff --git a/src/Skia/.gitignore b/src/Skia/.gitignore new file mode 100644 index 0000000000..1ea58c64aa --- /dev/null +++ b/src/Skia/.gitignore @@ -0,0 +1 @@ +native diff --git a/src/Skia/Perspex.Skia.Desktop/MethodTableImpl.cs b/src/Skia/Perspex.Skia.Desktop/MethodTableImpl.cs new file mode 100644 index 0000000000..e6d893d8ea --- /dev/null +++ b/src/Skia/Perspex.Skia.Desktop/MethodTableImpl.cs @@ -0,0 +1,233 @@ +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 GeneratePossiblePaths() + { + foreach ( + var basePath in + new[] {Assembly.GetEntryAssembly(), typeof (MethodTable).Assembly}.Select( + a => Path.GetDirectoryName(a.GetModules()[0].FullyQualifiedName)) + .Concat(new[] { Directory.GetCurrentDirectory()})) + { + 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; + } + } + } +} diff --git a/src/Skia/Perspex.Skia.Desktop/Perspex.Skia.Desktop.csproj b/src/Skia/Perspex.Skia.Desktop/Perspex.Skia.Desktop.csproj index 7087e92f53..6fb2549b69 100644 --- a/src/Skia/Perspex.Skia.Desktop/Perspex.Skia.Desktop.csproj +++ b/src/Skia/Perspex.Skia.Desktop/Perspex.Skia.Desktop.csproj @@ -43,6 +43,7 @@ + @@ -80,16 +81,24 @@ - + + native\Windows\i686\libEGL.dll Always - + + native\Windows\i686\libGLESv2.dll Always - + + native\Windows\i686\libperspesk.dll Always + + + native\Linux\x86_64\libperspesk.so + + diff --git a/src/Skia/Perspex.Skia.Desktop/libEGL.dll b/src/Skia/Perspex.Skia.Desktop/libEGL.dll deleted file mode 100644 index b22be6e83e..0000000000 Binary files a/src/Skia/Perspex.Skia.Desktop/libEGL.dll and /dev/null differ diff --git a/src/Skia/Perspex.Skia.Desktop/libGLESv2.dll b/src/Skia/Perspex.Skia.Desktop/libGLESv2.dll deleted file mode 100644 index dbb7175d0c..0000000000 Binary files a/src/Skia/Perspex.Skia.Desktop/libGLESv2.dll and /dev/null differ diff --git a/src/Skia/Perspex.Skia.Desktop/libperspesk.dll b/src/Skia/Perspex.Skia.Desktop/libperspesk.dll deleted file mode 100644 index e6877aafd7..0000000000 Binary files a/src/Skia/Perspex.Skia.Desktop/libperspesk.dll and /dev/null differ diff --git a/src/Skia/Perspex.Skia/MethodTable.cs b/src/Skia/Perspex.Skia/MethodTable.cs index 4cbb5c050c..d0da183c24 100644 --- a/src/Skia/Perspex.Skia/MethodTable.cs +++ b/src/Skia/Perspex.Skia/MethodTable.cs @@ -203,14 +203,8 @@ namespace Perspex.Skia } } - public static readonly MethodTable Instance = new Win32MethodTable(); + public static readonly MethodTable Instance = new MethodTableImpl(); } - class Win32MethodTable : MethodTable - { - [DllImport(@"libperspesk.dll")] - private static extern IntPtr GetPerspexMethodTable(); - public Win32MethodTable() : base(GetPerspexMethodTable()) { } - } } diff --git a/src/Skia/getnatives.ps1 b/src/Skia/getnatives.ps1 new file mode 100644 index 0000000000..3d54c7dd44 --- /dev/null +++ b/src/Skia/getnatives.ps1 @@ -0,0 +1,10 @@ +$scriptpath = $MyInvocation.MyCommand.Path +$dir = Split-Path $scriptpath +Push-Location $dir + +Add-Type -AssemblyName System.IO.Compression.FileSystem +rm -Force -Recurse .\native -ErrorAction SilentlyContinue +mkdir native +$url = cat native.url +Invoke-WebRequest $url -OutFile native\native.zip +[System.IO.Compression.ZipFile]::ExtractToDirectory("native\native.zip", "native") \ No newline at end of file diff --git a/src/Skia/native.url b/src/Skia/native.url new file mode 100644 index 0000000000..61e18108ae --- /dev/null +++ b/src/Skia/native.url @@ -0,0 +1 @@ +https://dl.dropboxusercontent.com/u/18301199/Perspex.Skia/abi-0001.zip