From cc60c6e5dd382920f0dcdd82d47241e939fff040 Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Tue, 31 Jan 2017 04:41:33 +0300 Subject: [PATCH] Implemented clipboard --- src/Gtk/Avalonia.Gtk3/Avalonia.Gtk3.csproj | 1 + src/Gtk/Avalonia.Gtk3/ClipboardImpl.cs | 53 +++++++++++++++++++++ src/Gtk/Avalonia.Gtk3/Gtk3Platform.cs | 2 +- src/Gtk/Avalonia.Gtk3/Interop/Native.cs | 23 +++++++++ src/Gtk/Avalonia.Gtk3/Interop/Utf8Buffer.cs | 13 +++++ src/Gtk/Avalonia.Gtk3/Stubs.cs | 9 ---- src/Gtk/Avalonia.Gtk3/TopLevelImpl.cs | 8 +--- 7 files changed, 92 insertions(+), 17 deletions(-) create mode 100644 src/Gtk/Avalonia.Gtk3/ClipboardImpl.cs 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; }