wayland: Implement reading the clipboard

We now keep track of what's in the clipboard and allow people to read
its contents.
This commit is contained in:
Benjamin Otte 2017-12-03 02:48:17 +01:00
parent 00192266a1
commit 82002eabfe
5 changed files with 168 additions and 34 deletions

View File

@ -20,12 +20,21 @@
#include "gdkclipboardprivate.h"
#include "gdkclipboard-wayland.h"
#include "gdkcontentformats.h"
#include "gdkintl.h"
#include "gdk-private.h"
#include <glib-unix.h>
#include <gio/gunixinputstream.h>
typedef struct _GdkWaylandClipboardClass GdkWaylandClipboardClass;
struct _GdkWaylandClipboard
{
GdkClipboard parent;
struct wl_data_offer *offer;
GdkContentFormats *offer_formats;
};
struct _GdkWaylandClipboardClass
@ -35,29 +44,131 @@ struct _GdkWaylandClipboardClass
G_DEFINE_TYPE (GdkWaylandClipboard, gdk_wayland_clipboard, GDK_TYPE_CLIPBOARD)
static void
gdk_wayland_clipboard_discard_offer (GdkWaylandClipboard *cb)
{
g_clear_pointer (&cb->offer_formats, gdk_content_formats_unref);
g_clear_pointer (&cb->offer, (GDestroyNotify) wl_data_offer_destroy);
}
static void
gdk_wayland_clipboard_finalize (GObject *object)
{
//GdkWaylandClipboard *cb = GDK_WAYLAND_CLIPBOARD (object);
GdkWaylandClipboard *cb = GDK_WAYLAND_CLIPBOARD (object);
gdk_wayland_clipboard_discard_offer (cb);
G_OBJECT_CLASS (gdk_wayland_clipboard_parent_class)->finalize (object);
}
static gboolean
gdk_wayland_clipboard_claim (GdkClipboard *clipboard,
GdkContentFormats *formats,
gboolean local,
GdkContentProvider *content)
{
GdkWaylandClipboard *cb = GDK_WAYLAND_CLIPBOARD (clipboard);
if (local)
{
/* not handled yet */
cb->offer = NULL;
}
return GDK_CLIPBOARD_CLASS (gdk_wayland_clipboard_parent_class)->claim (clipboard, formats, local, content);
}
static void
gdk_wayland_clipboard_read_async (GdkClipboard *clipboard,
GdkContentFormats *formats,
int io_priority,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
GdkWaylandClipboard *cb = GDK_WAYLAND_CLIPBOARD (clipboard);
GInputStream *stream;
const char *mime_type;
int pipe_fd[2];
GError *error = NULL;
GTask *task;
task = g_task_new (clipboard, cancellable, callback, user_data);
g_task_set_priority (task, io_priority);
g_task_set_source_tag (task, gdk_wayland_clipboard_read_async);
GDK_NOTE (CLIPBOARD, char *s = gdk_content_formats_to_string (formats);
g_printerr ("%p: read for %s\n", cb, s);
g_free (s); );
mime_type = gdk_content_formats_match_mime_type (formats, cb->offer_formats);
if (mime_type == NULL)
{
g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
_("No compatible transfer format found"));
return;
}
/* offer formats should be empty if we have no offer */
g_assert (cb->offer);
g_task_set_task_data (task, (gpointer) mime_type, NULL);
if (!g_unix_open_pipe (pipe_fd, FD_CLOEXEC, &error))
{
g_task_return_error (task, error);
return;
}
wl_data_offer_receive (cb->offer, mime_type, pipe_fd[1]);
stream = g_unix_input_stream_new (pipe_fd[0], TRUE);
close (pipe_fd[1]);
g_task_return_pointer (task, stream, g_object_unref);
}
static GInputStream *
gdk_wayland_clipboard_read_finish (GdkClipboard *clipboard,
const char **out_mime_type,
GAsyncResult *result,
GError **error)
{
GInputStream *stream;
GTask *task;
g_return_val_if_fail (g_task_is_valid (result, G_OBJECT (clipboard)), NULL);
task = G_TASK (result);
g_return_val_if_fail (g_task_get_source_tag (task) == gdk_wayland_clipboard_read_async, NULL);
stream = g_task_propagate_pointer (task, error);
if (stream)
{
if (out_mime_type)
*out_mime_type = g_task_get_task_data (task);
g_object_ref (stream);
}
else
{
if (out_mime_type)
*out_mime_type = NULL;
}
return stream;
}
static void
gdk_wayland_clipboard_class_init (GdkWaylandClipboardClass *class)
{
GObjectClass *object_class = G_OBJECT_CLASS (class);
//GdkClipboardClass *clipboard_class = GDK_CLIPBOARD_CLASS (class);
GdkClipboardClass *clipboard_class = GDK_CLIPBOARD_CLASS (class);
object_class->finalize = gdk_wayland_clipboard_finalize;
#if 0
clipboard_class->claim = gdk_wayland_clipboard_claim;
#if 0
clipboard_class->store_async = gdk_wayland_clipboard_store_async;
clipboard_class->store_finish = gdk_wayland_clipboard_store_finish;
#endif
clipboard_class->read_async = gdk_wayland_clipboard_read_async;
clipboard_class->read_finish = gdk_wayland_clipboard_read_finish;
#endif
}
static void
@ -76,3 +187,23 @@ gdk_wayland_clipboard_new (GdkDisplay *display)
return GDK_CLIPBOARD (cb);
}
void
gdk_wayland_clipboard_claim_remote (GdkWaylandClipboard *cb,
struct wl_data_offer *offer,
GdkContentFormats *formats)
{
g_return_if_fail (GDK_IS_WAYLAND_CLIPBOARD (cb));
gdk_wayland_clipboard_discard_offer (cb);
GDK_NOTE (CLIPBOARD, char *s = gdk_content_formats_to_string (formats);
g_printerr ("%p: remote clipboard claim for %s\n", cb, s);
g_free (s); );
cb->offer_formats = formats;
cb->offer = offer;
gdk_clipboard_claim_remote (GDK_CLIPBOARD (cb),
cb->offer_formats);
}

View File

@ -20,6 +20,7 @@
#include "gdk/gdkclipboard.h"
#include <wayland-client.h>
G_BEGIN_DECLS
@ -33,6 +34,9 @@ GType gdk_wayland_clipboard_get_type (void) G_GNU
GdkClipboard * gdk_wayland_clipboard_new (GdkDisplay *display);
void gdk_wayland_clipboard_claim_remote (GdkWaylandClipboard *cb,
struct wl_data_offer *offer,
GdkContentFormats *formats);
G_END_DECLS

View File

@ -216,7 +216,6 @@ struct _GdkWaylandSeat
GdkModifierType key_modifiers;
GdkWindow *keyboard_focus;
GdkAtom pending_selection;
GdkWindow *grab_window;
uint32_t grab_time;
gboolean have_server_repeat;
@ -1191,26 +1190,16 @@ data_device_selection (void *data,
struct wl_data_offer *offer)
{
GdkWaylandSeat *seat = data;
GdkAtom selection;
GdkContentFormats *formats;
GDK_NOTE (EVENTS,
g_message ("data device selection, data device %p, data offer %p",
wl_data_device, offer));
selection = gdk_atom_intern_static_string ("CLIPBOARD");
gdk_wayland_selection_set_offer (seat->display, selection, offer);
#if 0
/* If we already have keyboard focus, the selection was targeted at the
* focused surface. If we don't we will receive keyboard focus directly after
* this, so lets wait and find out what window will get the focus before
* emitting the owner-changed event.
*/
if (seat->keyboard_focus)
emit_selection_owner_change (seat->keyboard_focus, selection);
if (offer)
formats = gdk_wayland_selection_steal_offer (seat->display, offer);
else
seat->pending_selection = selection;
#endif
formats = gdk_content_formats_new (NULL, 0);
gdk_wayland_clipboard_claim_remote (GDK_WAYLAND_CLIPBOARD (seat->clipboard),
offer,
formats);
}
static const struct wl_data_device_listener data_device_listener = {
@ -1860,15 +1849,6 @@ keyboard_handle_enter (void *data,
seat, seat->keyboard_focus));
_gdk_wayland_display_deliver_event (seat->display, event);
/*
if (seat->pending_selection != NULL)
{
emit_selection_owner_change (seat->keyboard_focus,
seat->pending_selection);
seat->pending_selection = NULL;
}
*/
}
static void stop_key_repeat (GdkWaylandSeat *seat);
@ -4811,8 +4791,6 @@ _gdk_wayland_display_create_seat (GdkWaylandDisplay *display_wayland,
seat->foreign_dnd_window = create_foreign_dnd_window (display);
seat->wl_seat = wl_seat;
seat->pending_selection = NULL;
wl_seat_add_listener (seat->wl_seat, &seat_listener, seat);
wl_seat_set_user_data (seat->wl_seat, seat);

View File

@ -227,10 +227,12 @@ GdkWaylandSelection * gdk_wayland_display_get_selection (GdkDisplay *display);
GdkWaylandSelection * gdk_wayland_selection_new (void);
void gdk_wayland_selection_free (GdkWaylandSelection *selection);
void gdk_wayland_selection_ensure_offer (GdkDisplay *display,
struct wl_data_offer *wl_offer);
void gdk_wayland_selection_ensure_primary_offer (GdkDisplay *display,
struct gtk_primary_selection_offer *wp_offer);
GdkContentFormats *gdk_wayland_selection_steal_offer (GdkDisplay *display, gpointer wl_offer);
void gdk_wayland_selection_set_offer (GdkDisplay *display,
GdkAtom selection,

View File

@ -537,6 +537,25 @@ gdk_wayland_selection_ensure_primary_offer (GdkDisplay *
}
}
GdkContentFormats *
gdk_wayland_selection_steal_offer (GdkDisplay *display,
gpointer wl_offer)
{
GdkWaylandSelection *selection = gdk_wayland_display_get_selection (display);
GdkContentFormats *formats;
DataOfferData *info;
info = g_hash_table_lookup (selection->offers, wl_offer);
if (info == NULL)
return NULL;
g_hash_table_steal (selection->offers, wl_offer);
formats = info->targets;
g_slice_free (DataOfferData, info);
return formats;
}
void
gdk_wayland_selection_set_offer (GdkDisplay *display,
GdkAtom selection_atom,