diff --git a/src/Avalonia.X11/Interop/GtkInteropHelper.cs b/src/Avalonia.X11/Interop/GtkInteropHelper.cs new file mode 100644 index 00000000000..de0b7558327 --- /dev/null +++ b/src/Avalonia.X11/Interop/GtkInteropHelper.cs @@ -0,0 +1,15 @@ +using System; +using System.ComponentModel; +using System.Threading.Tasks; + +namespace Avalonia.X11.Interop; + +public class GtkInteropHelper +{ + public static async Task RunOnGlibThread(Func cb) + { + if (!await NativeDialogs.Gtk.StartGtk().ConfigureAwait(false)) + throw new Win32Exception("Unable to initialize GTK"); + return await NativeDialogs.Glib.RunOnGlibThread(cb).ConfigureAwait(false); + } +} \ No newline at end of file diff --git a/src/Avalonia.X11/NativeDialogs/Gtk.cs b/src/Avalonia.X11/NativeDialogs/Gtk.cs index 872c824f743..feca8c5e59f 100644 --- a/src/Avalonia.X11/NativeDialogs/Gtk.cs +++ b/src/Avalonia.X11/NativeDialogs/Gtk.cs @@ -3,6 +3,8 @@ using System.Threading; using System.Threading.Tasks; using Avalonia.Platform.Interop; +using JetBrains.Annotations; + // ReSharper disable IdentifierTypo namespace Avalonia.X11.NativeDialogs { @@ -247,10 +249,19 @@ public static extern void public static IntPtr GetForeignWindow(IntPtr xid) => gdk_x11_window_foreign_new_for_display(s_display, xid); + static object s_startGtkLock = new(); + static Task s_startGtkTask; + public static Task StartGtk() { - var tcs = new TaskCompletionSource(); - new Thread(() => + return StartGtkCore(); + lock (s_startGtkLock) + return s_startGtkTask ??= StartGtkCore(); + } + + private static void GtkThread(TaskCompletionSource tcs) + { + try { try { @@ -284,7 +295,17 @@ public static Task StartGtk() tcs.SetResult(true); while (true) gtk_main_iteration(); - }) {Name = "GTK3THREAD", IsBackground = true}.Start(); + } + catch + { + tcs.SetResult(false); + } + } + + private static Task StartGtkCore() + { + var tcs = new TaskCompletionSource(); + new Thread(() => GtkThread(tcs)) {Name = "GTK3THREAD", IsBackground = true}.Start(); return tcs.Task; } }