summaryrefslogtreecommitdiff
path: root/gdk/wayland
diff options
context:
space:
mode:
authorCarlos Garnacho <carlosg@gnome.org>2014-08-21 18:49:44 +0200
committerCarlos Garnacho <carlosg@gnome.org>2014-09-01 19:17:53 +0200
commit77447990728d171c53cf228acf9b99c2d8d132f0 (patch)
tree66c6f1c0f60217992b24a07dc5af7d368aeec0c5 /gdk/wayland
parent3b953041a9043d12238d8ca1b2d06a2ade702b1e (diff)
downloadgtk+-77447990728d171c53cf228acf9b99c2d8d132f0.tar.gz
wayland: Implement drag/source side of selections
This has been made to work similarly to X11, requests for the data device contents are notified through GDK_SELECTION_REQUEST events, the data stored in the GDK_SELECTION property as a reaction to that event is then stored into the wayland selection implementation, and written to the fd when requested/available. https://bugzilla.gnome.org/show_bug.cgi?id=697855
Diffstat (limited to 'gdk/wayland')
-rw-r--r--gdk/wayland/gdkdevice-wayland.c26
-rw-r--r--gdk/wayland/gdkprivate-wayland.h13
-rw-r--r--gdk/wayland/gdkselection-wayland.c322
-rw-r--r--gdk/wayland/gdkwindow-wayland.c2
4 files changed, 362 insertions, 1 deletions
diff --git a/gdk/wayland/gdkdevice-wayland.c b/gdk/wayland/gdkdevice-wayland.c
index a44f384dc3..36980b3d9e 100644
--- a/gdk/wayland/gdkdevice-wayland.c
+++ b/gdk/wayland/gdkdevice-wayland.c
@@ -2173,3 +2173,29 @@ gdk_wayland_device_unset_touch_grab (GdkDevice *gdk_device,
_gdk_display_end_touch_grab (gdk_device_get_display (gdk_device),
gdk_device, sequence);
}
+
+struct wl_data_device *
+gdk_wayland_device_get_data_device (GdkDevice *gdk_device)
+{
+ GdkWaylandDeviceData *device;
+
+ g_return_val_if_fail (GDK_IS_WAYLAND_DEVICE (gdk_device), NULL);
+ device = GDK_WAYLAND_DEVICE (gdk_device)->device;
+
+ return device->data_device;
+}
+
+void
+gdk_wayland_device_set_selection (GdkDevice *gdk_device,
+ struct wl_data_source *source)
+{
+ GdkWaylandDeviceData *device;
+ GdkWaylandDisplay *display_wayland;
+
+ g_return_if_fail (GDK_IS_WAYLAND_DEVICE (gdk_device));
+ device = GDK_WAYLAND_DEVICE (gdk_device)->device;
+ display_wayland = GDK_WAYLAND_DISPLAY (gdk_device_get_display (gdk_device));
+
+ wl_data_device_set_selection (device->data_device, source,
+ _gdk_wayland_display_get_serial (display_wayland));
+}
diff --git a/gdk/wayland/gdkprivate-wayland.h b/gdk/wayland/gdkprivate-wayland.h
index c04d2579f1..b72c7a64d7 100644
--- a/gdk/wayland/gdkprivate-wayland.h
+++ b/gdk/wayland/gdkprivate-wayland.h
@@ -147,6 +147,9 @@ uint32_t _gdk_wayland_device_get_implicit_grab_serial(GdkWaylandDevice *device,
const GdkEvent *event);
uint32_t _gdk_wayland_device_get_last_implicit_grab_serial (GdkWaylandDevice *device,
GdkEventSequence **seqence);
+struct wl_data_device * gdk_wayland_device_get_data_device (GdkDevice *gdk_device);
+void gdk_wayland_device_set_selection (GdkDevice *gdk_device,
+ struct wl_data_source *source);
void gdk_wayland_device_unset_touch_grab (GdkDevice *device,
GdkEventSequence *sequence);
@@ -201,4 +204,14 @@ void gdk_wayland_selection_set_offer (struct wl_data_offer *offer);
struct wl_data_offer * gdk_wayland_selection_get_offer (void);
GList * gdk_wayland_selection_get_targets (void);
+void gdk_wayland_selection_store (GdkWindow *window,
+ GdkAtom type,
+ GdkPropMode mode,
+ const guchar *data,
+ gint len);
+struct wl_data_source * gdk_wayland_selection_get_data_source (GdkWindow *owner,
+ GdkAtom selection);
+void gdk_wayland_selection_unset_data_source (GdkAtom selection);
+
+
#endif /* __GDK_PRIVATE_WAYLAND_H__ */
diff --git a/gdk/wayland/gdkselection-wayland.c b/gdk/wayland/gdkselection-wayland.c
index b3383305e5..1a75dd03c0 100644
--- a/gdk/wayland/gdkselection-wayland.c
+++ b/gdk/wayland/gdkselection-wayland.c
@@ -60,6 +60,13 @@ struct _DataSourceData
GdkAtom selection;
};
+enum {
+ ATOM_CLIPBOARD,
+ ATOM_DND
+};
+
+static GdkAtom atoms[2] = { 0 };
+
struct _GdkWaylandSelection
{
/* Destination-side data */
@@ -245,6 +252,10 @@ gdk_wayland_selection_new (void)
{
GdkWaylandSelection *selection;
+ /* init atoms */
+ atoms[ATOM_CLIPBOARD] = gdk_atom_intern_static_string ("CLIPBOARD");
+ atoms[ATOM_DND] = gdk_atom_intern_static_string ("GdkWaylandSelection");
+
selection = g_new0 (GdkWaylandSelection, 1);
selection->selection_buffers = g_hash_table_new_full (NULL, NULL, NULL,
(GDestroyNotify) selection_buffer_cancel_and_unref);
@@ -335,6 +346,103 @@ gdk_wayland_selection_get_targets (void)
return selection->targets;
}
+void
+gdk_wayland_selection_emit_request (GdkWindow *window,
+ GdkAtom selection,
+ GdkAtom target)
+{
+ GdkEvent *event;
+
+ event = gdk_event_new (GDK_SELECTION_REQUEST);
+ event->selection.window = g_object_ref (window);
+ event->selection.send_event = FALSE;
+ event->selection.selection = selection;
+ event->selection.target = target;
+ event->selection.property = gdk_atom_intern_static_string ("GDK_SELECTION");
+ event->selection.time = GDK_CURRENT_TIME;
+ event->selection.requestor = g_object_ref (window);
+
+ gdk_event_put (event);
+ gdk_event_free (event);
+}
+
+gboolean
+gdk_wayland_selection_check_write (GdkWaylandSelection *selection)
+{
+ gssize len, bytes_written = 0;
+ gchar *buf;
+
+ if (selection->stored_selection.fd < 0 ||
+ selection->stored_selection.data_len == 0)
+ return FALSE;
+
+ len = selection->stored_selection.data_len;
+ buf = (gchar *) selection->stored_selection.data;
+
+ while (len > 0)
+ {
+ bytes_written += write (selection->stored_selection.fd,
+ buf + bytes_written, len);
+
+ if (bytes_written < 0)
+ break;
+
+ len -= bytes_written;
+ }
+
+ close (selection->stored_selection.fd);
+ selection->stored_selection.fd = -1;
+
+ return bytes_written != 0;
+}
+
+void
+gdk_wayland_selection_store (GdkWindow *window,
+ GdkAtom type,
+ GdkPropMode mode,
+ const guchar *data,
+ gint len)
+{
+ GdkDisplay *display = gdk_display_get_default ();
+ GdkWaylandSelection *selection = gdk_wayland_display_get_selection (display);
+ GArray *array;
+
+ array = g_array_new (TRUE, FALSE, sizeof (guchar));
+ g_array_append_vals (array, data, len);
+
+ if (selection->stored_selection.data)
+ {
+ if (mode != GDK_PROP_MODE_REPLACE &&
+ type != selection->stored_selection.type)
+ {
+ g_warning (G_STRLOC ": Attempted to append/prepend selection data with "
+ "type %s into the current selection with type %s",
+ gdk_atom_name (type),
+ gdk_atom_name (selection->stored_selection.type));
+ return;
+ }
+
+ /* In these cases we also replace the stored data, so we
+ * apply the inverse operation into the just given data.
+ */
+ if (mode == GDK_PROP_MODE_APPEND)
+ g_array_prepend_vals (array, selection->stored_selection.data,
+ selection->stored_selection.data_len - 1);
+ else if (mode == GDK_PROP_MODE_PREPEND)
+ g_array_append_vals (array, selection->stored_selection.data,
+ selection->stored_selection.data_len - 1);
+
+ g_free (selection->stored_selection.data);
+ }
+
+ selection->stored_selection.source = window;
+ selection->stored_selection.data_len = array->len;
+ selection->stored_selection.data = (guchar *) g_array_free (array, FALSE);
+ selection->stored_selection.type = type;
+
+ gdk_wayland_selection_check_write (selection);
+}
+
static SelectionBuffer *
gdk_wayland_selection_lookup_requestor_buffer (GdkWindow *requestor)
{
@@ -354,6 +462,205 @@ gdk_wayland_selection_lookup_requestor_buffer (GdkWindow *requestor)
return NULL;
}
+static gboolean
+gdk_wayland_selection_request_target (GdkWaylandSelection *wayland_selection,
+ GdkWindow *window,
+ GdkAtom target,
+ gint fd)
+{
+ GdkAtom selection;
+
+ if (wayland_selection->clipboard_owner == window)
+ selection = atoms[ATOM_CLIPBOARD];
+ else if (wayland_selection->dnd_owner == window)
+ selection = atoms[ATOM_DND];
+ else
+ return FALSE;
+
+ if (fd >= 0)
+ wayland_selection->stored_selection.fd = fd;
+
+ if (wayland_selection->source_requested_target == target)
+ return FALSE;
+
+ wayland_selection->source_requested_target = target;
+
+ if (window && target != GDK_NONE)
+ {
+ gdk_wayland_selection_emit_request (window, selection, target);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static void
+data_source_target (void *data,
+ struct wl_data_source *source,
+ const char *mime_type)
+{
+ GdkWaylandSelection *wayland_selection = data;
+ GdkDragContext *context;
+ GdkWindow *window;
+
+ g_debug (G_STRLOC ": %s source = %p, mime_type = %s",
+ G_STRFUNC, source, mime_type);
+
+ if (!mime_type)
+ return;
+
+ if (source == wayland_selection->dnd_source)
+ window = wayland_selection->dnd_owner;
+ else if (source == wayland_selection->clipboard_source)
+ window = wayland_selection->clipboard_owner;
+ else
+ return;
+
+ gdk_wayland_selection_request_target (wayland_selection, window,
+ gdk_atom_intern (mime_type, FALSE),
+ -1);
+}
+
+static void
+data_source_send (void *data,
+ struct wl_data_source *source,
+ const char *mime_type,
+ int32_t fd)
+{
+ GdkWaylandSelection *wayland_selection = data;
+ GdkWindow *window;
+
+ g_debug (G_STRLOC ": %s source = %p, mime_type = %s, fd = %d",
+ G_STRFUNC, source, mime_type, fd);
+
+ if (!mime_type)
+ return;
+
+ if (source == wayland_selection->dnd_source)
+ window = wayland_selection->dnd_owner;
+ else if (source == wayland_selection->clipboard_source)
+ window = wayland_selection->clipboard_owner;
+ else
+ return;
+
+ if (!gdk_wayland_selection_request_target (wayland_selection, window,
+ gdk_atom_intern (mime_type, FALSE),
+ fd))
+ gdk_wayland_selection_check_write (wayland_selection);
+
+ wayland_selection->source_requested_target = GDK_NONE;
+}
+
+static void
+data_source_cancelled (void *data,
+ struct wl_data_source *source)
+{
+ GdkWaylandSelection *wayland_selection = data;
+
+ g_debug (G_STRLOC ": %s source = %p",
+ G_STRFUNC, source);
+
+ if (source == wayland_selection->dnd_source)
+ gdk_wayland_selection_unset_data_source (atoms[ATOM_DND]);
+ else if (source == wayland_selection->clipboard_source)
+ gdk_wayland_selection_unset_data_source (atoms[ATOM_CLIPBOARD]);
+}
+
+static const struct wl_data_source_listener data_source_listener = {
+ data_source_target,
+ data_source_send,
+ data_source_cancelled
+};
+
+struct wl_data_source *
+gdk_wayland_selection_get_data_source (GdkWindow *owner,
+ GdkAtom selection)
+{
+ GdkDisplay *display = gdk_display_get_default ();
+ GdkWaylandSelection *wayland_selection = gdk_wayland_display_get_selection (display);
+ struct wl_data_source *source = NULL;
+ GdkWaylandDisplay *display_wayland;
+ gboolean is_clipboard = FALSE;
+
+ if (selection == atoms[ATOM_DND])
+ {
+ if (wayland_selection->dnd_source &&
+ (!owner || owner == wayland_selection->dnd_owner))
+ return wayland_selection->dnd_source;
+ }
+ else if (selection == atoms[ATOM_CLIPBOARD])
+ {
+ if (wayland_selection->clipboard_source &&
+ (!owner || owner == wayland_selection->clipboard_owner))
+ return wayland_selection->clipboard_source;
+
+ if (wayland_selection->clipboard_source)
+ {
+ wl_data_source_destroy (wayland_selection->clipboard_source);
+ wayland_selection->clipboard_source = NULL;
+ }
+
+ is_clipboard = TRUE;
+ }
+ else
+ return NULL;
+
+ if (!owner)
+ return NULL;
+
+ display_wayland = GDK_WAYLAND_DISPLAY (gdk_window_get_display (owner));
+
+ source = wl_data_device_manager_create_data_source (display_wayland->data_device_manager);
+ wl_data_source_add_listener (source,
+ &data_source_listener,
+ wayland_selection);
+
+ if (is_clipboard)
+ {
+ wayland_selection->clipboard_source = source;
+ wayland_selection->clipboard_owner = owner;
+ }
+ else
+ {
+ wayland_selection->dnd_source = source;
+ wayland_selection->dnd_owner = owner;
+ }
+
+ return source;
+}
+
+void
+gdk_wayland_selection_unset_data_source (GdkAtom selection)
+{
+ GdkDisplay *display = gdk_display_get_default ();
+ GdkWaylandSelection *wayland_selection = gdk_wayland_display_get_selection (display);
+
+ if (selection == atoms[ATOM_CLIPBOARD])
+ {
+ GdkDeviceManager *device_manager;
+ GdkDisplay *display;
+ GdkDevice *device;
+
+ display = gdk_display_get_default ();
+ device_manager = gdk_display_get_device_manager (display);
+ device = gdk_device_manager_get_client_pointer (device_manager);
+
+ gdk_wayland_device_set_selection (device, NULL);
+ wayland_selection->clipboard_owner = NULL;
+
+ if (wayland_selection->clipboard_source)
+ {
+ wl_data_source_destroy (wayland_selection->clipboard_source);
+ wayland_selection->clipboard_source = NULL;
+ }
+ }
+ else if (selection == atoms[ATOM_DND])
+ {
+ wayland_selection->dnd_owner = NULL;
+ wayland_selection->dnd_source = NULL;
+ }
+}
+
GdkWindow *
_gdk_wayland_display_get_selection_owner (GdkDisplay *display,
GdkAtom selection)
@@ -375,7 +682,20 @@ _gdk_wayland_display_set_selection_owner (GdkDisplay *display,
guint32 time,
gboolean send_event)
{
- return TRUE;
+ GdkWaylandSelection *wayland_selection = gdk_wayland_display_get_selection (display);
+
+ if (selection == atoms[ATOM_CLIPBOARD])
+ {
+ wayland_selection->clipboard_owner = owner;
+ return TRUE;
+ }
+ else if (selection == atoms[ATOM_DND])
+ {
+ wayland_selection->dnd_owner = owner;
+ return TRUE;
+ }
+
+ return FALSE;
}
void
diff --git a/gdk/wayland/gdkwindow-wayland.c b/gdk/wayland/gdkwindow-wayland.c
index e8b2ecf2e1..f96b6b8c68 100644
--- a/gdk/wayland/gdkwindow-wayland.c
+++ b/gdk/wayland/gdkwindow-wayland.c
@@ -1949,6 +1949,8 @@ gdk_wayland_window_change_property (GdkWindow *window,
const guchar *data,
gint nelements)
{
+ if (property == gdk_atom_intern_static_string ("GDK_SELECTION"))
+ gdk_wayland_selection_store (window, type, mode, data, nelements * (format / 8));
}
static void