5 changed files with 156 additions and 13 deletions
@ -0,0 +1,115 @@ |
|||||
|
using System; |
||||
|
using System.Runtime.InteropServices; |
||||
|
using System.Threading; |
||||
|
using System.Threading.Tasks; |
||||
|
using Avalonia.Controls; |
||||
|
using Avalonia.Controls.Platform; |
||||
|
using Avalonia.Gtk3.Interop; |
||||
|
using Avalonia.Platform; |
||||
|
using Avalonia.Platform.Interop; |
||||
|
|
||||
|
namespace Avalonia.Gtk3 |
||||
|
{ |
||||
|
public class Gtk3ForeignX11SystemDialog : ISystemDialogImpl |
||||
|
{ |
||||
|
private Task<bool> _initialized; |
||||
|
private SystemDialogBase _inner = new SystemDialogBase(); |
||||
|
|
||||
|
|
||||
|
public async Task<string[]> ShowFileDialogAsync(FileDialog dialog, IWindowImpl parent) |
||||
|
{ |
||||
|
await EnsureInitialized(); |
||||
|
var xid = parent.Handle.Handle; |
||||
|
return await await RunOnGtkThread( |
||||
|
() => _inner.ShowFileDialogAsync(dialog, GtkWindow.Null, chooser => UpdateParent(chooser, xid))); |
||||
|
} |
||||
|
|
||||
|
public async Task<string> ShowFolderDialogAsync(OpenFolderDialog dialog, IWindowImpl parent) |
||||
|
{ |
||||
|
await EnsureInitialized(); |
||||
|
var xid = parent.Handle.Handle; |
||||
|
return await await RunOnGtkThread( |
||||
|
() => _inner.ShowFolderDialogAsync(dialog, GtkWindow.Null, chooser => UpdateParent(chooser, xid))); |
||||
|
} |
||||
|
|
||||
|
void UpdateParent(GtkFileChooser chooser, IntPtr xid) |
||||
|
{ |
||||
|
Native.GtkWidgetRealize(chooser); |
||||
|
var window = Native.GtkWidgetGetWindow(chooser); |
||||
|
var parent = Native.GdkWindowForeignNewForDisplay(GdkDisplay, xid); |
||||
|
if (window != IntPtr.Zero && parent != IntPtr.Zero) |
||||
|
Native.GdkWindowSetTransientFor(window, parent); |
||||
|
} |
||||
|
|
||||
|
async Task EnsureInitialized() |
||||
|
{ |
||||
|
if (_initialized == null) |
||||
|
{ |
||||
|
var tcs = new TaskCompletionSource<bool>(); |
||||
|
_initialized = tcs.Task; |
||||
|
new Thread(() => GtkThread(tcs)) |
||||
|
{ |
||||
|
IsBackground = true |
||||
|
}.Start(); |
||||
|
} |
||||
|
|
||||
|
if (!(await _initialized)) |
||||
|
throw new Exception("Unable to initialize GTK on separate thread"); |
||||
|
|
||||
|
} |
||||
|
|
||||
|
Task<T> RunOnGtkThread<T>(Func<T> action) |
||||
|
{ |
||||
|
var tcs = new TaskCompletionSource<T>(); |
||||
|
GlibTimeout.Add(0, 0, () => |
||||
|
{ |
||||
|
|
||||
|
try |
||||
|
{ |
||||
|
tcs.SetResult(action()); |
||||
|
} |
||||
|
catch (Exception e) |
||||
|
{ |
||||
|
tcs.TrySetException(e); |
||||
|
} |
||||
|
|
||||
|
return false; |
||||
|
}); |
||||
|
return tcs.Task; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
void GtkThread(TaskCompletionSource<bool> tcs) |
||||
|
{ |
||||
|
try |
||||
|
{ |
||||
|
X11.XInitThreads(); |
||||
|
}catch{} |
||||
|
Resolver.Resolve(); |
||||
|
if (Native.GdkWindowForeignNewForDisplay == null) |
||||
|
throw new Exception("gdk_x11_window_foreign_new_for_display is not found in your libgdk-3.so"); |
||||
|
using (var backends = new Utf8Buffer("x11")) |
||||
|
Native.GdkSetAllowedBackends?.Invoke(backends); |
||||
|
if (!Native.GtkInitCheck(0, IntPtr.Zero)) |
||||
|
{ |
||||
|
tcs.SetResult(false); |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
using (var utf = new Utf8Buffer($"avalonia.app.a{Guid.NewGuid().ToString("N")}")) |
||||
|
App = Native.GtkApplicationNew(utf, 0); |
||||
|
if (App == IntPtr.Zero) |
||||
|
{ |
||||
|
tcs.SetResult(false); |
||||
|
return; |
||||
|
} |
||||
|
GdkDisplay = Native.GdkGetDefaultDisplay(); |
||||
|
tcs.SetResult(true); |
||||
|
while (true) |
||||
|
Native.GtkMainIteration(); |
||||
|
} |
||||
|
|
||||
|
private IntPtr GdkDisplay { get; set; } |
||||
|
private IntPtr App { get; set; } |
||||
|
} |
||||
|
} |
||||
Loading…
Reference in new issue