Browse Source

Implemented framebuffer and smarter dll search logic

pull/875/head
Nikita Tsukanov 9 years ago
parent
commit
5bb9e6913e
  1. 2
      samples/ControlCatalog.Desktop/Program.cs
  2. 3
      src/Gtk/Avalonia.Gtk3/Avalonia.Gtk3.csproj
  3. 42
      src/Gtk/Avalonia.Gtk3/FramebufferManager.cs
  4. 8
      src/Gtk/Avalonia.Gtk3/Gtk3Platform.cs
  5. 71
      src/Gtk/Avalonia.Gtk3/ImageSurfaceFramebuffer.cs
  6. 33
      src/Gtk/Avalonia.Gtk3/Interop/DynLoader.cs
  7. 17
      src/Gtk/Avalonia.Gtk3/Interop/ICustomGtk3NativeLibraryResolver.cs
  8. 36
      src/Gtk/Avalonia.Gtk3/Interop/Native.cs
  9. 113
      src/Gtk/Avalonia.Gtk3/Interop/Resolver.cs
  10. 8
      src/Gtk/Avalonia.Gtk3/TopLevelImpl.cs

2
samples/ControlCatalog.Desktop/Program.cs

@ -18,7 +18,7 @@ namespace ControlCatalog
// TODO: Make this work with GTK/Skia/Cairo depending on command-line args
// again.
AppBuilder.Configure<App>()
.UseDirect2D1()
.UseSkia()
.UseGtk3()
.Start<MainWindow>();
}

3
src/Gtk/Avalonia.Gtk3/Avalonia.Gtk3.csproj

@ -41,13 +41,16 @@
<None Include="project.json" />
</ItemGroup>
<ItemGroup>
<Compile Include="FramebufferManager.cs" />
<Compile Include="Gtk3Platform.cs" />
<Compile Include="Interop\ICustomGtk3NativeLibraryResolver.cs" />
<Compile Include="Interop\DynLoader.cs" />
<Compile Include="Interop\GlibTimeout.cs" />
<Compile Include="Interop\Native.cs" />
<Compile Include="Interop\NativeException.cs" />
<Compile Include="Interop\Resolver.cs" />
<Compile Include="Interop\Signal.cs" />
<Compile Include="ImageSurfaceFramebuffer.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Stubs.cs" />
<Compile Include="TopLevelImpl.cs" />

42
src/Gtk/Avalonia.Gtk3/FramebufferManager.cs

@ -0,0 +1,42 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Avalonia.Controls.Platform.Surfaces;
namespace Avalonia.Gtk3
{
class FramebufferManager : IFramebufferPlatformSurface, IDisposable
{
private readonly TopLevelImpl _window;
private ImageSurfaceFramebuffer _fb;
public FramebufferManager(TopLevelImpl window)
{
_window = window;
}
public void Dispose()
{
_fb?.Deallocate();
}
public ILockedFramebuffer Lock()
{
if(_window.CurrentCairoContext == IntPtr.Zero)
throw new InvalidOperationException("Window is not in drawing state");
var ctx = _window.CurrentCairoContext;
var width = (int) _window.ClientSize.Width;
var height = (int) _window.ClientSize.Height;
if (_fb == null || _fb.Width != width ||
_fb.Height != height)
{
_fb?.Dispose();
_fb = new ImageSurfaceFramebuffer(width, height);
}
_fb.Prepare(ctx);
return _fb;
}
}
}

8
src/Gtk/Avalonia.Gtk3/Gtk3Platform.cs

@ -104,7 +104,11 @@ namespace Avalonia.Gtk3
public static class Gtk3AppBuilderExtensions
{
public static T UseGtk3<T>(this AppBuilderBase<T> builder) where T : AppBuilderBase<T>, new()
=> builder.UseWindowingSubsystem(Gtk3Platform.Initialize, "GTK3");
public static T UseGtk3<T>(this AppBuilderBase<T> builder, ICustomGtk3NativeLibraryResolver resolver = null)
where T : AppBuilderBase<T>, new()
{
Resolver.Custom = resolver;
return builder.UseWindowingSubsystem(Gtk3Platform.Initialize, "GTK3");
}
}
}

71
src/Gtk/Avalonia.Gtk3/ImageSurfaceFramebuffer.cs

@ -0,0 +1,71 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Avalonia.Controls.Platform.Surfaces;
using Avalonia.Gtk3.Interop;
using Avalonia.Platform;
namespace Avalonia.Gtk3
{
class ImageSurfaceFramebuffer : ILockedFramebuffer
{
private IntPtr _context;
private IntPtr _surface;
public ImageSurfaceFramebuffer(int width, int height)
{
_surface = Native.CairoImageSurfaceCreate(1, width, height);
Width = width;
Height = height;
Address = Native.CairoImageSurfaceGetData(_surface);
RowBytes = Native.CairoImageSurfaceGetStride(_surface);
Native.CairoSurfaceFlush(_surface);
}
public void Prepare(IntPtr context)
{
_context = context;
}
public void Deallocate()
{
Native.CairoSurfaceDestroy(_surface);
_surface = IntPtr.Zero;
}
public void Dispose()
{
if(_context == IntPtr.Zero || _surface == IntPtr.Zero)
return;
Native.CairoSurfaceMarkDirty(_surface);
Native.CairoSetSourceSurface(_context, _surface, 0, 0);
Native.CairoPaint(_context);
_context = IntPtr.Zero;
}
public IntPtr Address { get; }
public int Width { get; }
public int Height { get; }
public int RowBytes { get; }
//TODO: Proper DPI detect
public Size Dpi => new Size(96, 96);
public PixelFormat Format
{
get
{
if (AvaloniaLocator.Current.GetService<IRuntimePlatform>().GetRuntimeInfo().OperatingSystem ==
OperatingSystemType.WinNT)
return PixelFormat.Bgra8888;
return PixelFormat.Rgba8888;
}
}
}
}

33
src/Gtk/Avalonia.Gtk3/Interop/DynLoader.cs

@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
/*
@ -11,7 +12,7 @@ namespace Avalonia.Gtk3.Interop
{
internal interface IDynLoader
{
IntPtr LoadLibrary(string basePath, string dll);
IntPtr LoadLibrary(string dll);
IntPtr GetProcAddress(IntPtr dll, string proc);
}
@ -21,7 +22,6 @@ namespace Avalonia.Gtk3.Interop
// ReSharper disable InconsistentNaming
static class LinuxImports
{
[DllImport("libdl.so.2")]
private static extern IntPtr dlopen(string path, int flags);
@ -36,7 +36,6 @@ namespace Avalonia.Gtk3.Interop
DlOpen = dlopen;
DlSym = dlsym;
DlError = dlerror;
Postfix = ".so.0";
}
}
static class OsXImports
@ -57,7 +56,6 @@ namespace Avalonia.Gtk3.Interop
DlOpen = dlopen;
DlSym = dlsym;
DlError = dlerror;
Postfix = ".dylib"; //TODO
}
}
@ -66,8 +64,6 @@ namespace Avalonia.Gtk3.Interop
[DllImport("libc")]
static extern int uname(IntPtr buf);
static UnixLoader()
{
var buffer = Marshal.AllocHGlobal(0x1000);
@ -83,20 +79,12 @@ namespace Avalonia.Gtk3.Interop
private static Func<string, int, IntPtr> DlOpen;
private static Func<IntPtr, string, IntPtr> DlSym;
private static Func<IntPtr> DlError;
private static string Postfix;
// ReSharper restore InconsistentNaming
static string DlErrorString()
{
return Marshal.PtrToStringAnsi(DlError());
}
static string DlErrorString() => Marshal.PtrToStringAnsi(DlError());
public IntPtr LoadLibrary(string basePath, string dll)
public IntPtr LoadLibrary(string dll)
{
dll += Postfix;
if (basePath != null)
dll = System.IO.Path.Combine(basePath, dll);
var handle = DlOpen(dll, 1);
if (handle == IntPtr.Zero)
throw new NativeException(DlErrorString());
@ -120,15 +108,14 @@ namespace Avalonia.Gtk3.Interop
[DllImport("kernel32", EntryPoint = "LoadLibraryW", SetLastError = true, CharSet = CharSet.Unicode)]
private static extern IntPtr LoadLibrary(string lpszLib);
IntPtr IDynLoader.LoadLibrary(string basePath, string dll)
IntPtr IDynLoader.LoadLibrary(string dll)
{
dll += "-0.dll";
if (basePath != null)
dll = System.IO.Path.Combine(basePath, dll);
var handle = LoadLibrary(dll);
if (handle == IntPtr.Zero)
throw new NativeException("Error " + Marshal.GetLastWin32Error());
return handle;
if (handle != IntPtr.Zero)
return handle;
var err = Marshal.GetLastWin32Error();
throw new NativeException("Error loading " + dll + " error " + err);
}
IntPtr IDynLoader.GetProcAddress(IntPtr dll, string proc)

17
src/Gtk/Avalonia.Gtk3/Interop/ICustomGtk3NativeLibraryResolver.cs

@ -0,0 +1,17 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Avalonia.Gtk3.Interop;
namespace Avalonia.Gtk3
{
public interface ICustomGtk3NativeLibraryResolver
{
string GetName(GtkDll dll);
string BasePath { get; }
bool TrySystemFirst { get; }
string Lookup(GtkDll dll);
}
}

36
src/Gtk/Avalonia.Gtk3/Interop/Native.cs

@ -80,6 +80,33 @@ namespace Avalonia.Gtk3.Interop
[UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)]
public delegate void gtk_window_move(IntPtr gtkWindow, int x, int y);
[UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Cairo)]
public delegate IntPtr cairo_image_surface_create(int format, int width, int height);
[UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Cairo)]
public delegate IntPtr cairo_image_surface_get_data(IntPtr surface);
[UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Cairo)]
public delegate int cairo_image_surface_get_stride(IntPtr surface);
[UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Cairo)]
public delegate void cairo_surface_mark_dirty(IntPtr surface);
[UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Cairo)]
public delegate void cairo_surface_flush(IntPtr surface);
[UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Cairo)]
public delegate void cairo_surface_destroy(IntPtr surface);
[UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Cairo)]
public delegate void cairo_set_source_surface(IntPtr cr, IntPtr surface, double x, double y);
[UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Cairo)]
public delegate void cairo_paint(IntPtr context);
[UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)]
public delegate void gtk_widget_queue_draw_area(IntPtr gtkWindow, int x, int y, int width, int height);
@ -130,7 +157,14 @@ namespace Avalonia.Gtk3.Interop
public static D.gdk_screen_get_width GdkScreenGetWidth;
public static D.gdk_window_get_origin GdkWindowGetOrigin;
public static D.cairo_image_surface_create CairoImageSurfaceCreate;
public static D.cairo_image_surface_get_data CairoImageSurfaceGetData;
public static D.cairo_image_surface_get_stride CairoImageSurfaceGetStride;
public static D.cairo_surface_mark_dirty CairoSurfaceMarkDirty;
public static D.cairo_surface_flush CairoSurfaceFlush;
public static D.cairo_surface_destroy CairoSurfaceDestroy;
public static D.cairo_set_source_surface CairoSetSourceSurface;
public static D.cairo_paint CairoPaint;
}
public enum GtkWindowType

113
src/Gtk/Avalonia.Gtk3/Interop/Resolver.cs

@ -1,10 +1,12 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using Avalonia.Platform;
namespace Avalonia.Gtk3.Interop
{
@ -22,64 +24,108 @@ namespace Avalonia.Gtk3.Interop
}
}
internal enum GtkDll
public enum GtkDll
{
Gdk,
Gtk,
Glib,
Gio,
Gobject
Gobject,
Cairo
}
static class Resolver
{
[DllImport("kernel32.dll")]
static extern int GetVersion();
private static Lazy<OperatingSystemType> Platform =
new Lazy<OperatingSystemType>(
() => AvaloniaLocator.Current.GetService<IRuntimePlatform>().GetRuntimeInfo().OperatingSystem);
static bool IsWin32()
public static ICustomGtk3NativeLibraryResolver Custom { get; set; }
static string FormatName(string name, int version = 0)
{
if (Platform.Value == OperatingSystemType.WinNT)
return "lib" + name + "-" + version + ".dll";
if (Platform.Value == OperatingSystemType.Linux)
return "lib" + name + ".so" + "." + version;
if (Platform.Value == OperatingSystemType.OSX)
return "lib" + name + "." + version + ".dylib";
throw new Exception("Unknown platform, use custom name resolver");
}
static string GetDllName(GtkDll dll)
{
var name = Custom?.GetName(dll);
if (name != null)
return name;
switch (dll)
{
case GtkDll.Cairo:
return FormatName("cairo", 2);
case GtkDll.Gdk:
return FormatName("gdk-3");
case GtkDll.Glib:
return FormatName("glib-2.0");
case GtkDll.Gio:
return FormatName("gio-2.0");
case GtkDll.Gtk:
return FormatName("gtk-3");
case GtkDll.Gobject:
return FormatName("gobject-2.0");
default:
throw new ArgumentException("Unknown lib: " + dll);
}
}
static IntPtr LoadDll(IDynLoader loader, GtkDll dll)
{
var exceptions = new List<Exception>();
var name = GetDllName(dll);
if (Custom?.TrySystemFirst != false)
{
try
{
return loader.LoadLibrary(name);
}
catch (Exception e)
{
exceptions.Add(e);
}
}
var path = Custom?.Lookup(dll);
if (path == null && Custom?.BasePath != null)
path = Path.Combine(Custom.BasePath, name);
try
{
GetVersion();
return true;
return loader.LoadLibrary(path);
}
catch
catch (Exception e)
{
return false;
exceptions.Add(e);
}
throw new AggregateException("Unable to load " + dll, exceptions);
}
public static void Resolve(string basePath = null)
{
var loader = IsWin32() ? (IDynLoader)new Win32Loader() : new UnixLoader();
var loader = Platform.Value == OperatingSystemType.WinNT ? (IDynLoader)new Win32Loader() : new UnixLoader();
var gdk = loader.LoadLibrary(basePath, "libgdk-3");
var gtk = loader.LoadLibrary(basePath, "libgtk-3");
var gio = loader.LoadLibrary(basePath, "libgio-2.0");
var glib = loader.LoadLibrary(basePath, "libglib-2.0");
var gobject = loader.LoadLibrary(basePath, "libgobject-2.0");
var dlls = Enum.GetValues(typeof(GtkDll)).Cast<GtkDll>().ToDictionary(x => x, x => LoadDll(loader, x));
foreach (var fieldInfo in typeof(Native).GetTypeInfo().DeclaredFields)
{
var import = fieldInfo.FieldType.GetTypeInfo().GetCustomAttributes(typeof(GtkImportAttribute), true).Cast<GtkImportAttribute>().FirstOrDefault();
if(import == null)
continue;
IntPtr lib;
if (import.Dll == GtkDll.Gtk)
lib = gtk;
else if (import.Dll == GtkDll.Gdk)
lib = gdk;
else if (import.Dll == GtkDll.Gio)
lib = gio;
else if (import.Dll == GtkDll.Glib)
lib = glib;
else if (import.Dll == GtkDll.Gobject)
lib = gobject;
else
throw new ArgumentException("Invalid GtkImportAttribute for " + fieldInfo.FieldType);
IntPtr lib = dlls[import.Dll];
var funcPtr = loader.GetProcAddress(lib, import.Name ?? fieldInfo.FieldType.Name);
fieldInfo.SetValue(null, Marshal.GetDelegateForFunctionPointer(funcPtr, fieldInfo.FieldType));
}
@ -90,7 +136,7 @@ namespace Avalonia.Gtk3.Interop
try
{
Native.GetNativeGdkWindowHandle = (Native.D.gdk_get_native_handle)Marshal
.GetDelegateForFunctionPointer(loader.GetProcAddress(gdk, name), typeof(Native.D.gdk_get_native_handle));
.GetDelegateForFunctionPointer(loader.GetProcAddress(dlls[GtkDll.Gdk], name), typeof(Native.D.gdk_get_native_handle));
break;
}
catch { }
@ -103,3 +149,4 @@ namespace Avalonia.Gtk3.Interop
}
}

8
src/Gtk/Avalonia.Gtk3/TopLevelImpl.cs

@ -13,11 +13,13 @@ namespace Avalonia.Gtk3
{
protected readonly IntPtr GtkWidget;
private IInputRoot _inputRoot;
private readonly FramebufferManager _framebuffer;
protected readonly List<IDisposable> _disposables = new List<IDisposable>();
public TopLevelImpl(IntPtr gtkWidget)
{
GtkWidget = gtkWidget;
_framebuffer = new FramebufferManager(this);
Native.GtkWidgetSetEvents(gtkWidget, uint.MaxValue);
Native.GtkWidgetRealize(gtkWidget);
Connect<Native.D.signal_widget_draw>("draw", OnDraw);
@ -126,9 +128,13 @@ namespace Avalonia.Gtk3
void Connect<T>(string name, T handler) => _disposables.Add(Signal.Connect<T>(GtkWidget, name, handler));
internal IntPtr CurrentCairoContext { get; private set; }
private bool OnDraw(IntPtr gtkwidget, IntPtr cairocontext, IntPtr userdata)
{
CurrentCairoContext = cairocontext;
Paint?.Invoke(new Rect(ClientSize));
CurrentCairoContext = IntPtr.Zero;
return true;
}
@ -234,6 +240,6 @@ namespace Avalonia.Gtk3
}
IntPtr IPlatformHandle.Handle => Native.GetNativeGdkWindowHandle(Native.GtkWidgetGetWindow(GtkWidget));
public IEnumerable<object> Surfaces => new object[] {Handle};
public IEnumerable<object> Surfaces => new object[] {Handle, _framebuffer};
}
}

Loading…
Cancel
Save