Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 40 additions & 0 deletions src/Avalonia.Controls.WebView.Core/Gtk/GtkInterop.cs
Original file line number Diff line number Diff line change
Expand Up @@ -345,6 +345,46 @@ internal static extern void gdk_x11_window_set_frame_sync_enabled(IntPtr window,

[DllImport(LibGdk)]
internal static extern IntPtr gdk_display_get_default();
// GObject type plumbing, used by GtkOffscreenHostWindow to register a GtkWindow subtype at runtime.
[StructLayout(LayoutKind.Sequential)]
internal struct GTypeQuery
{
public IntPtr type;
public IntPtr type_name;
public uint class_size;
public uint instance_size;
}

[DllImport(LibGObject)]
internal static extern unsafe void g_type_query(IntPtr type, GTypeQuery* query);
[LibraryImport(LibGObject, StringMarshalling = StringMarshalling.Utf8)]
internal static partial IntPtr g_type_from_name(string name);
[DllImport(LibGObject)]
internal static extern IntPtr g_type_class_ref(IntPtr type);
[DllImport(LibGObject)]
internal static extern IntPtr g_type_class_peek(IntPtr type);
[DllImport(LibGObject)]
internal static extern IntPtr g_initially_unowned_get_type();
[LibraryImport(LibGObject, StringMarshalling = StringMarshalling.Utf8)]
internal static partial IntPtr g_type_register_static_simple(IntPtr parentType, string typeName, uint classSize,
IntPtr classInit, uint instanceSize, IntPtr instanceInit, int flags);
[DllImport(LibGObject)]
internal static extern IntPtr g_object_new_with_properties(IntPtr type, uint nProperties, IntPtr names, IntPtr values);

[DllImport(LibGtk)]
internal static extern IntPtr gtk_window_get_type();
[DllImport(LibGtk)]
internal static extern IntPtr gtk_offscreen_window_get_type();

[DllImport(LibGdk)]
internal static extern IntPtr gdk_offscreen_window_get_surface(IntPtr window);
[DllImport(LibGdk)]
internal static extern int gdk_window_get_width(IntPtr window);
[DllImport(LibGdk)]
internal static extern int gdk_window_get_height(IntPtr window);
[DllImport(LibGdk)]
internal static extern IntPtr gdk_pixbuf_get_from_surface(IntPtr surface, int x, int y, int width, int height);

[DllImport(LibGdk)]
internal static extern IntPtr gdk_display_get_default_seat(IntPtr display);
[DllImport(LibGdk)]
Expand Down
93 changes: 93 additions & 0 deletions src/Avalonia.Controls.WebView.Core/Gtk/GtkOffscreenHostWindow.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using static Avalonia.Controls.Gtk.GtkInterop;

namespace Avalonia.Controls.Gtk;

internal static unsafe class GtkOffscreenHostWindow
{
private const string TypeName = "AvaloniaWebViewOffscreenHostWindow";

private static IntPtr s_type;
private static bool s_registrationFailed;

public static IntPtr TryCreate()
{
var type = EnsureRegistered();
return type == IntPtr.Zero ? IntPtr.Zero : g_object_new_with_properties(type, 0, IntPtr.Zero, IntPtr.Zero);
}

public static IntPtr GetPixbuf(IntPtr window)
{
var gdkWindow = gtk_widget_get_window(window);
if (gdkWindow == IntPtr.Zero)
return IntPtr.Zero;

var surface = gdk_offscreen_window_get_surface(gdkWindow);
if (surface == IntPtr.Zero)
return IntPtr.Zero;

return gdk_pixbuf_get_from_surface(surface, 0, 0, gdk_window_get_width(gdkWindow), gdk_window_get_height(gdkWindow));
}

private static IntPtr EnsureRegistered()
{
if (s_type != IntPtr.Zero || s_registrationFailed)
return s_type;

try
{
var existing = g_type_from_name(TypeName);
if (existing != IntPtr.Zero)
return s_type = existing;

g_type_class_ref(gtk_window_get_type());
g_type_class_ref(gtk_offscreen_window_get_type());

GTypeQuery windowQuery;
g_type_query(gtk_window_get_type(), &windowQuery);
if (windowQuery.class_size == 0 || windowQuery.instance_size == 0)
{
s_registrationFailed = true;
return IntPtr.Zero;
}

s_type = g_type_register_static_simple(gtk_window_get_type(), TypeName, windowQuery.class_size,
(IntPtr)(delegate* unmanaged[Cdecl]<IntPtr, IntPtr, void>)&ClassInit, windowQuery.instance_size,
IntPtr.Zero, 0);
}
catch (EntryPointNotFoundException)
{
s_type = IntPtr.Zero;
}
catch (DllNotFoundException)
{
s_type = IntPtr.Zero;
}

s_registrationFailed = s_type == IntPtr.Zero;
return s_type;
}

[UnmanagedCallersOnly(CallConvs = [typeof(CallConvCdecl)])]
private static void ClassInit(IntPtr klass, IntPtr data)
{
var windowClass = (IntPtr*)g_type_class_peek(gtk_window_get_type());
var offscreenClass = (IntPtr*)g_type_class_peek(gtk_offscreen_window_get_type());
var ownClass = (IntPtr*)klass;

GTypeQuery objectQuery, windowQuery;
g_type_query(g_initially_unowned_get_type(), &objectQuery);
g_type_query(gtk_window_get_type(), &windowQuery);

var first = (int)(objectQuery.class_size / (uint)IntPtr.Size);
var end = (int)(windowQuery.class_size / (uint)IntPtr.Size);

for (var i = first; i < end; i++)
{
if (ownClass[i] == windowClass[i] && offscreenClass[i] != windowClass[i])
ownClass[i] = offscreenClass[i];
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,26 @@ internal abstract unsafe class GtkOffscreenWebViewAdapter : GtkWebViewAdapter,
new((delegate* unmanaged[Cdecl]<IntPtr, IntPtr*, IntPtr, int>)&DrawCallback);

private readonly bool _experimentalOffscreen;
private readonly bool _usesHostWindow;
private IntPtr _windowHandle;
private PixelSize _sizeRequest;
private GtkSignal? _drawSignal;

protected GtkOffscreenWebViewAdapter(GtkWebViewEnvironmentRequestedEventArgs args) : base(args)
{
_experimentalOffscreen = args.ExperimentalOffscreen;
_windowHandle = args.ExperimentalOffscreen ? gtk_offscreen_window_new() : gtk_window_new(0 /* GTK_WINDOW_TOPLEVEL */);
if (args.ExperimentalOffscreen)
{
// Not a real GtkOffscreenWindow, so WebKit lets media start.
_windowHandle = GtkOffscreenHostWindow.TryCreate();
_usesHostWindow = _windowHandle != IntPtr.Zero;
if (!_usesHostWindow)
_windowHandle = gtk_offscreen_window_new();
}
else
{
_windowHandle = gtk_window_new(0);
}
g_object_ref_sink(_windowHandle);
gtk_window_set_default_size(_windowHandle, 100, 100);

Expand Down Expand Up @@ -62,7 +74,9 @@ public Task UpdateWriteableBitmap(PixelSize _, FrameChainBase<WriteableBitmap, P
IntPtr pixbuf;
if (_experimentalOffscreen)
{
pixbuf = gtk_offscreen_window_get_pixbuf(_windowHandle);
pixbuf = _usesHostWindow
? GtkOffscreenHostWindow.GetPixbuf(_windowHandle)
: gtk_offscreen_window_get_pixbuf(_windowHandle);
}
else
{
Expand Down
Loading