-
-
Notifications
You must be signed in to change notification settings - Fork 2.3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Implemented XEmbed client support with GtkSharp usage example (#17446)
- Loading branch information
Showing
15 changed files
with
722 additions
and
47 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
using System.Runtime.InteropServices; | ||
|
||
namespace XEmbedSample; | ||
|
||
/* | ||
This is needed specifically for GtkSharp: | ||
https://github.com/mono/SkiaSharp/issues/3038 | ||
https://github.com/GtkSharp/GtkSharp/issues/443 | ||
Instead of using plain DllImport they are manually calling dlopen with RTLD_GLOBAL and RTLD_LAZY flags: | ||
https://github.com/GtkSharp/GtkSharp/blob/b7303616129ab5a0ca64def45649ab522d83fa4a/Source/Libs/Shared/FuncLoader.cs#L80-L92 | ||
Which causes libHarfBuzzSharp.so from HarfBuzzSharp to resolve some of the symbols from the system libharfbuzz.so.0 | ||
which is a _different_ harfbuzz version. | ||
That results in a segfault. | ||
Previously there was a workaround - https://github.com/mono/SkiaSharp/pull/2247 but it got | ||
disabled for .NET Core / .NET 5+. | ||
Why linux linker builds shared libraries in a way that makes it possible for them to resolve their own symbols from | ||
elsewhere escapes me. | ||
Here we are loading libHarfBuzzSharp.so from the .NET-resolved location, saving it, unloading the library | ||
and then defining a custom resolver that would call dlopen with RTLD_NOW + RTLD_DEEPBIND | ||
*/ | ||
|
||
public unsafe class HarfbuzzWorkaround | ||
{ | ||
[DllImport("libc")] | ||
static extern int dlinfo(IntPtr handle, int request, IntPtr info); | ||
|
||
[DllImport("libc")] | ||
static extern IntPtr dlopen(string filename, int flags); | ||
|
||
private const int RTLD_DI_ORIGIN = 6; | ||
private const int RTLD_NOW = 2; | ||
private const int RTLD_DEEPBIND = 8; | ||
|
||
public static void Apply() | ||
{ | ||
if (RuntimeInformation.RuntimeIdentifier.Contains("musl")) | ||
throw new PlatformNotSupportedException("musl doesn't support RTLD_DEEPBIND"); | ||
|
||
var libraryPathBytes = Marshal.AllocHGlobal(4096); | ||
var handle = NativeLibrary.Load("libHarfBuzzSharp", typeof(HarfBuzzSharp.Blob).Assembly, null); | ||
dlinfo(handle, RTLD_DI_ORIGIN, libraryPathBytes); | ||
var libraryOrigin = Marshal.PtrToStringUTF8(libraryPathBytes); | ||
Marshal.FreeHGlobal(libraryPathBytes); | ||
var libraryPath = Path.Combine(libraryOrigin, "libHarfBuzzSharp.so"); | ||
|
||
NativeLibrary.Free(handle); | ||
var forceLoadedHandle = dlopen(libraryPath, RTLD_NOW | RTLD_DEEPBIND); | ||
if (forceLoadedHandle == IntPtr.Zero) | ||
throw new DllNotFoundException($"Unable to load {libraryPath} via dlopen"); | ||
|
||
NativeLibrary.SetDllImportResolver(typeof(HarfBuzzSharp.Blob).Assembly, (name, assembly, searchPath) => | ||
{ | ||
if (name.Contains("HarfBuzzSharp")) | ||
return dlopen(libraryPath, RTLD_NOW | RTLD_DEEPBIND); | ||
return NativeLibrary.Load(name, assembly, searchPath); | ||
}); | ||
|
||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
using Avalonia; | ||
using Avalonia.Controls; | ||
using Avalonia.Controls.Primitives; | ||
using Avalonia.Media; | ||
using ControlCatalog; | ||
using ControlCatalog.Models; | ||
using Gtk; | ||
|
||
namespace XEmbedSample; | ||
|
||
class Program | ||
{ | ||
static void Main(string[] args) | ||
{ | ||
HarfbuzzWorkaround.Apply(); | ||
AppBuilder.Configure<App>() | ||
.UseSkia() | ||
.With(new X11PlatformOptions() | ||
{ | ||
UseGLibMainLoop = true, | ||
ExterinalGLibMainLoopExceptionLogger = e => Console.WriteLine(e.ToString()) | ||
}) | ||
.UseX11() | ||
.SetupWithoutStarting(); | ||
App.SetCatalogThemes(CatalogTheme.Fluent); | ||
Gdk.Global.AllowedBackends = "x11"; | ||
Gtk.Application.Init("myapp", ref args); | ||
|
||
|
||
|
||
|
||
|
||
var w = new Gtk.Window("XEmbed Test Window"); | ||
var socket = new AvaloniaXEmbedGtkSocket(w.StyleContext.GetBackgroundColor(StateFlags.Normal)) | ||
{ | ||
Content = new ScrollViewer() | ||
{ | ||
Content = new ControlCatalog.Pages.TextBoxPage(), | ||
HorizontalScrollBarVisibility = ScrollBarVisibility.Auto | ||
} | ||
}; | ||
var vbox = new Gtk.Box(Gtk.Orientation.Vertical, 5); | ||
var label = new Gtk.Label("Those are GTK controls"); | ||
vbox.Add(label); | ||
vbox.Add(new Gtk.Entry()); | ||
vbox.Add(new Gtk.Button(new Gtk.Label("Do nothing"))); | ||
vbox.PackEnd(socket, true, true, 0); | ||
socket.HeightRequest = 400; | ||
socket.WidthRequest = 400; | ||
w.Add(vbox); | ||
socket.Realize(); | ||
|
||
|
||
w.AddSignalHandler("destroy", new EventHandler((_, __) => | ||
{ | ||
Gtk.Application.Quit(); | ||
socket.Destroy(); | ||
})); | ||
w.ShowAll(); | ||
Gtk.Application.Run(); | ||
|
||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
using Avalonia; | ||
using Avalonia.X11; | ||
using Gdk; | ||
using Color = Cairo.Color; | ||
|
||
namespace XEmbedSample; | ||
|
||
public class AvaloniaXEmbedGtkSocket : Gtk.Socket | ||
{ | ||
private readonly RGBA _backgroundColor; | ||
private XEmbedPlug? _avaloniaPlug; | ||
public AvaloniaXEmbedGtkSocket(RGBA backgroundColor) | ||
{ | ||
_backgroundColor = backgroundColor; | ||
} | ||
|
||
private object _content; | ||
public object Content | ||
{ | ||
get => _content; | ||
set | ||
{ | ||
_content = value; | ||
if (_avaloniaPlug != null) | ||
_avaloniaPlug.Content = _content; | ||
} | ||
} | ||
|
||
protected override void OnRealized() | ||
{ | ||
base.OnRealized(); | ||
_avaloniaPlug ??= XEmbedPlug.Create(); | ||
_avaloniaPlug.ScaleFactor = ScaleFactor; | ||
_avaloniaPlug.BackgroundColor = Avalonia.Media.Color.FromRgb((byte)(_backgroundColor.Red * 255), | ||
(byte)(_backgroundColor.Green * 255), | ||
(byte)(_backgroundColor.Blue * 255) | ||
); | ||
_avaloniaPlug.Content = _content; | ||
ApplyInteractiveResize(); | ||
AddId((ulong)_avaloniaPlug.Handle); | ||
} | ||
|
||
void ApplyInteractiveResize() | ||
{ | ||
// This is _NOT_ a part of XEmbed, but allows us to have smooth resize | ||
GetAllocatedSize(out var rect, out _); | ||
var scale = ScaleFactor; | ||
_avaloniaPlug?.ProcessInteractiveResize(new PixelSize(rect.Width * scale, rect.Height * scale)); | ||
} | ||
|
||
protected override void OnSizeAllocated(Rectangle allocation) | ||
{ | ||
base.OnSizeAllocated(allocation); | ||
Display.Default.Sync(); | ||
ApplyInteractiveResize(); | ||
} | ||
|
||
protected override void OnDestroyed() | ||
{ | ||
_avaloniaPlug?.Dispose(); | ||
_avaloniaPlug = null; | ||
base.OnDestroyed(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
<Project Sdk="Microsoft.NET.Sdk"> | ||
|
||
<PropertyGroup> | ||
<OutputType>Exe</OutputType> | ||
<TargetFramework>net6.0</TargetFramework> | ||
<ImplicitUsings>enable</ImplicitUsings> | ||
<Nullable>enable</Nullable> | ||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks> | ||
</PropertyGroup> | ||
|
||
<ItemGroup> | ||
<PackageReference Include="GtkSharp" Version="3.24.24.95" /> | ||
</ItemGroup> | ||
|
||
<ItemGroup> | ||
<ProjectReference Include="..\..\src\Avalonia.X11\Avalonia.X11.csproj" /> | ||
<ProjectReference Include="..\ControlCatalog\ControlCatalog.csproj" /> | ||
</ItemGroup> | ||
|
||
</Project> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
using Avalonia.Threading; | ||
|
||
namespace Avalonia.X11.Dispatching; | ||
|
||
interface IX11PlatformDispatcher : IDispatcherImpl | ||
{ | ||
X11EventDispatcher EventDispatcher { get; } | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.