diff --git a/src/Gtk/Avalonia.Gtk3/Avalonia.Gtk3.csproj b/src/Gtk/Avalonia.Gtk3/Avalonia.Gtk3.csproj
index e0e773aaa6..910b0f0d52 100644
--- a/src/Gtk/Avalonia.Gtk3/Avalonia.Gtk3.csproj
+++ b/src/Gtk/Avalonia.Gtk3/Avalonia.Gtk3.csproj
@@ -44,6 +44,7 @@
KeyTransform.cs
+
diff --git a/src/Gtk/Avalonia.Gtk3/ClipboardImpl.cs b/src/Gtk/Avalonia.Gtk3/ClipboardImpl.cs
new file mode 100644
index 0000000000..333391a064
--- /dev/null
+++ b/src/Gtk/Avalonia.Gtk3/ClipboardImpl.cs
@@ -0,0 +1,53 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Runtime.InteropServices;
+using System.Text;
+using System.Threading.Tasks;
+using Avalonia.Gtk3.Interop;
+using Avalonia.Input.Platform;
+
+namespace Avalonia.Gtk3
+{
+ class ClipboardImpl : IClipboard
+ {
+
+ IntPtr GetClipboard() => Native.GtkClipboardGetForDisplay(Native.GdkGetDefaultDisplay(), IntPtr.Zero);
+
+ static void OnText(IntPtr clipboard, IntPtr utf8string, IntPtr userdata)
+ {
+ var handle = GCHandle.FromIntPtr(userdata);
+
+ ((TaskCompletionSource) handle.Target)
+ .TrySetResult(Utf8Buffer.StringFromPtr(utf8string));
+ handle.Free();
+ }
+
+ private static readonly Native.D.GtkClipboardTextReceivedFunc OnTextDelegate = OnText;
+
+ static ClipboardImpl()
+ {
+ GCHandle.Alloc(OnTextDelegate);
+ }
+
+ public Task GetTextAsync()
+ {
+ var tcs = new TaskCompletionSource();
+ Native.GtkClipboardRequestText(GetClipboard(), OnTextDelegate, GCHandle.ToIntPtr(GCHandle.Alloc(tcs)));
+ return tcs.Task;
+ }
+
+ public Task SetTextAsync(string text)
+ {
+ using (var buf = new Utf8Buffer(text))
+ Native.GtkClipboardSetText(GetClipboard(), buf, buf.ByteLen);
+ return Task.FromResult(0);
+ }
+
+ public Task ClearAsync()
+ {
+ Native.GtkClipboardRequestClear(GetClipboard());
+ return Task.FromResult(0);
+ }
+ }
+}
diff --git a/src/Gtk/Avalonia.Gtk3/Gtk3Platform.cs b/src/Gtk/Avalonia.Gtk3/Gtk3Platform.cs
index 81aac5e2dc..296f302556 100644
--- a/src/Gtk/Avalonia.Gtk3/Gtk3Platform.cs
+++ b/src/Gtk/Avalonia.Gtk3/Gtk3Platform.cs
@@ -30,7 +30,7 @@ namespace Avalonia.Gtk3
s_tlsMarker = true;
AvaloniaLocator.CurrentMutable.Bind().ToConstant(Instance)
- .Bind().ToSingleton()
+ .Bind().ToSingleton()
.Bind().ToConstant(new CursorFactoryStub())
.Bind().ToConstant(Keyboard)
.Bind().ToConstant(Mouse)
diff --git a/src/Gtk/Avalonia.Gtk3/Interop/Native.cs b/src/Gtk/Avalonia.Gtk3/Interop/Native.cs
index 736ec7e191..f3c1c847ca 100644
--- a/src/Gtk/Avalonia.Gtk3/Interop/Native.cs
+++ b/src/Gtk/Avalonia.Gtk3/Interop/Native.cs
@@ -57,6 +57,9 @@ namespace Avalonia.Gtk3.Interop
[UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gdk)]
public delegate int gdk_screen_get_width(IntPtr screen);
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gdk)]
+ public delegate IntPtr gdk_display_get_default();
+
[UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gdk)]
public delegate int gdk_window_get_origin(IntPtr gdkWindow, out int x, out int y);
[UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gdk)]
@@ -145,6 +148,18 @@ namespace Avalonia.Gtk3.Interop
[UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gdk)]
public delegate void gdk_window_begin_resize_drag(IntPtr window, WindowEdge edge, gint button, gint root_x, gint root_y, guint32 timestamp);
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)]
+ public delegate IntPtr gtk_clipboard_get_for_display(IntPtr display, IntPtr atom);
+
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)]
+ public delegate void gtk_clipboard_request_text(IntPtr clipboard, GtkClipboardTextReceivedFunc callback, IntPtr user_data);
+
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)]
+ public delegate void gtk_clipboard_set_text(IntPtr clipboard, Utf8Buffer text, int len);
+
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)]
+ public delegate void gtk_clipboard_clear(IntPtr clipboard);
+
[UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gobject)]
public delegate ulong g_signal_connect_object(IntPtr instance, Utf8Buffer signal, IntPtr handler, IntPtr userData, int flags);
[UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gobject)]
@@ -165,6 +180,9 @@ namespace Avalonia.Gtk3.Interop
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate bool timeout_callback(IntPtr data);
+
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+ public delegate void GtkClipboardTextReceivedFunc(IntPtr clipboard, IntPtr utf8string, IntPtr userdata);
}
@@ -195,6 +213,10 @@ namespace Avalonia.Gtk3.Interop
public static D.gdk_window_invalidate_rect GdkWindowInvalidateRect;
public static D.gtk_widget_queue_draw_area GtkWidgetQueueDrawArea;
public static D.gtk_widget_activate GtkWidgetActivate;
+ public static D.gtk_clipboard_get_for_display GtkClipboardGetForDisplay;
+ public static D.gtk_clipboard_request_text GtkClipboardRequestText;
+ public static D.gtk_clipboard_set_text GtkClipboardSetText;
+ public static D.gtk_clipboard_clear GtkClipboardRequestClear;
public static D.gtk_im_multicontext_new GtkImMulticontextNew;
@@ -202,6 +224,7 @@ namespace Avalonia.Gtk3.Interop
public static D.gtk_im_context_set_client_window GtkImContextSetClientWindow;
public static D.gdk_screen_get_height GdkScreenGetHeight;
+ public static D.gdk_display_get_default GdkGetDefaultDisplay;
public static D.gdk_screen_get_width GdkScreenGetWidth;
public static D.gdk_screen_get_root_window GdkScreenGetRootWindow;
public static D.gdk_window_get_origin GdkWindowGetOrigin;
diff --git a/src/Gtk/Avalonia.Gtk3/Interop/Utf8Buffer.cs b/src/Gtk/Avalonia.Gtk3/Interop/Utf8Buffer.cs
index 7a9868857f..f108c291b8 100644
--- a/src/Gtk/Avalonia.Gtk3/Interop/Utf8Buffer.cs
+++ b/src/Gtk/Avalonia.Gtk3/Interop/Utf8Buffer.cs
@@ -16,6 +16,8 @@ namespace Avalonia.Gtk3.Interop
handle = _gchandle.AddrOfPinnedObject();
}
+ public int ByteLen => _data.Length;
+
protected override bool ReleaseHandle()
{
if (handle != IntPtr.Zero)
@@ -28,5 +30,16 @@ namespace Avalonia.Gtk3.Interop
}
public override bool IsInvalid => handle == IntPtr.Zero;
+
+ public static unsafe string StringFromPtr(IntPtr s)
+ {
+ var pstr = (byte*)s;
+ int len;
+ for (len = 0; pstr[len] != 0; len++) ;
+ var bytes = new byte[len];
+ Marshal.Copy(s, bytes, 0, len);
+
+ return Encoding.UTF8.GetString(bytes, 0, len);
+ }
}
}
diff --git a/src/Gtk/Avalonia.Gtk3/Stubs.cs b/src/Gtk/Avalonia.Gtk3/Stubs.cs
index 76333aba60..89d9251cdd 100644
--- a/src/Gtk/Avalonia.Gtk3/Stubs.cs
+++ b/src/Gtk/Avalonia.Gtk3/Stubs.cs
@@ -14,15 +14,6 @@ using Avalonia.Platform;
namespace Avalonia.Gtk3
{
- class ClipboardStub : IClipboard
- {
- public Task GetTextAsync() => Task.FromResult("");
-
- public Task SetTextAsync(string text) => Task.FromResult(0);
-
- public Task ClearAsync() => Task.FromResult(0);
- }
-
class CursorFactoryStub : IStandardCursorFactory
{
public IPlatformHandle GetCursor(StandardCursorType cursorType) => new PlatformHandle(IntPtr.Zero, "STUB");
diff --git a/src/Gtk/Avalonia.Gtk3/TopLevelImpl.cs b/src/Gtk/Avalonia.Gtk3/TopLevelImpl.cs
index 989a0584fa..e532a05915 100644
--- a/src/Gtk/Avalonia.Gtk3/TopLevelImpl.cs
+++ b/src/Gtk/Avalonia.Gtk3/TopLevelImpl.cs
@@ -170,13 +170,7 @@ namespace Avalonia.Gtk3
private unsafe bool OnCommit(IntPtr gtkwidget, IntPtr utf8string, IntPtr userdata)
{
- var pstr = (byte*)utf8string;
- int len;
- for (len = 0; pstr[len] != 0; len++) ;
- var bytes = new byte[len];
- Marshal.Copy(utf8string, bytes, 0, len);
-
- Input(new RawTextInputEventArgs(Gtk3Platform.Keyboard, _lastKbdEvent, Encoding.UTF8.GetString(bytes, 0, len)));
+ Input(new RawTextInputEventArgs(Gtk3Platform.Keyboard, _lastKbdEvent, Utf8Buffer.StringFromPtr(utf8string)));
return true;
}