From 1178a0ac8bcbdf50d85298e606abd3f64b06bbe4 Mon Sep 17 00:00:00 2001 From: Carlos Garnacho Date: Tue, 7 Apr 2015 16:40:57 +0200 Subject: wayland: Split handling of clipboard/DnD data offers We currently only hold the last offer received, which is wrong, as both are independent and have different life cycles. This means we have to store per-selection wl_data_offer and targets, and maintain these as appropriate from the clipboard/DnD specific entry points. --- gdk/wayland/gdkdevice-wayland.c | 19 +++-- gdk/wayland/gdkdnd-wayland.c | 21 +++-- gdk/wayland/gdkprivate-wayland.h | 9 ++- gdk/wayland/gdkselection-wayland.c | 155 +++++++++++++++++++++++++++---------- 4 files changed, 148 insertions(+), 56 deletions(-) diff --git a/gdk/wayland/gdkdevice-wayland.c b/gdk/wayland/gdkdevice-wayland.c index 345266f554..1e17c77487 100644 --- a/gdk/wayland/gdkdevice-wayland.c +++ b/gdk/wayland/gdkdevice-wayland.c @@ -688,8 +688,7 @@ data_device_data_offer (void *data, g_message ("data device data offer, data device %p, offer %p", data_device, offer)); - gdk_wayland_selection_set_offer (device->display, offer); - emit_selection_owner_change_forall (gdk_atom_intern_static_string ("GdkWaylandSelection")); + gdk_wayland_selection_ensure_offer (device->display, offer); } static void @@ -703,6 +702,7 @@ data_device_enter (void *data, { GdkWaylandDeviceData *device = (GdkWaylandDeviceData *)data; GdkWindow *dest_window, *dnd_owner; + GdkAtom selection; dest_window = wl_surface_get_user_data (surface); @@ -720,7 +720,8 @@ data_device_enter (void *data, gdk_wayland_drop_context_update_targets (device->drop_context); - dnd_owner = gdk_selection_owner_get_for_display (device->display, gdk_drag_get_selection (device->drop_context)); + selection = gdk_drag_get_selection (device->drop_context); + dnd_owner = gdk_selection_owner_get_for_display (device->display, selection); if (!dnd_owner) dnd_owner = device->foreign_dnd_window; @@ -734,9 +735,9 @@ data_device_enter (void *data, wl_fixed_to_double (y)); _gdk_wayland_drag_context_emit_event (device->drop_context, GDK_DRAG_ENTER, GDK_CURRENT_TIME); - gdk_wayland_selection_set_offer (device->display, offer); - emit_selection_owner_change (dest_window, - gdk_atom_intern_static_string ("GdkWaylandSelection")); + + gdk_wayland_selection_set_offer (device->display, selection, offer); + emit_selection_owner_change_forall (selection); } static void @@ -811,13 +812,15 @@ data_device_selection (void *data, struct wl_data_offer *offer) { GdkWaylandDeviceData *device = (GdkWaylandDeviceData *) data; + GdkAtom selection; GDK_NOTE (EVENTS, g_message ("data device selection, data device %p, data offer %p", wl_data_device, offer)); - gdk_wayland_selection_set_offer (device->display, offer); - emit_selection_owner_change_forall (gdk_atom_intern_static_string ("CLIPBOARD")); + selection = gdk_atom_intern_static_string ("CLIPBOARD"); + gdk_wayland_selection_set_offer (device->display, selection, offer); + emit_selection_owner_change_forall (selection); } static const struct wl_data_device_listener data_device_listener = { diff --git a/gdk/wayland/gdkdnd-wayland.c b/gdk/wayland/gdkdnd-wayland.c index 6bbe3ced09..e96aeda337 100644 --- a/gdk/wayland/gdkdnd-wayland.c +++ b/gdk/wayland/gdkdnd-wayland.c @@ -43,7 +43,6 @@ struct _GdkWaylandDragContext GdkWindow *dnd_window; struct wl_surface *dnd_surface; struct wl_data_source *data_source; - struct wl_data_offer *offer; uint32_t serial; gdouble x; gdouble y; @@ -196,8 +195,10 @@ gdk_wayland_drop_context_set_status (GdkDragContext *context, struct wl_data_offer *wl_offer; context_wayland = GDK_WAYLAND_DRAG_CONTEXT (context); - display = gdk_window_get_display (context->source_window); - wl_offer = gdk_wayland_selection_get_offer (display); + + display = gdk_device_get_display (gdk_drag_context_get_device (context)); + wl_offer = gdk_wayland_selection_get_offer (display, + gdk_drag_get_selection (context)); if (!wl_offer) return; @@ -247,10 +248,15 @@ gdk_wayland_drag_context_drop_finish (GdkDragContext *context, gboolean success, guint32 time) { - GdkDisplay *display = gdk_window_get_display (context->source_window); + GdkDisplay *display = gdk_device_get_display (gdk_drag_context_get_device (context)); + GdkAtom selection; - if (gdk_selection_owner_get_for_display (display, gdk_drag_get_selection (context))) - gdk_wayland_selection_unset_data_source (display, gdk_drag_get_selection (context)); + selection = gdk_drag_get_selection (context); + + if (gdk_selection_owner_get_for_display (display, selection)) + gdk_wayland_selection_unset_data_source (display, selection); + + gdk_wayland_selection_set_offer (display, selection, NULL); } static gboolean @@ -396,7 +402,8 @@ gdk_wayland_drop_context_update_targets (GdkDragContext *context) device = gdk_drag_context_get_device (context); display = gdk_device_get_display (device); g_list_free (context->targets); - context->targets = g_list_copy (gdk_wayland_selection_get_targets (display)); + context->targets = g_list_copy (gdk_wayland_selection_get_targets (display, + gdk_drag_get_selection (context))); } void diff --git a/gdk/wayland/gdkprivate-wayland.h b/gdk/wayland/gdkprivate-wayland.h index beb489a8c0..cd36c2e4e1 100644 --- a/gdk/wayland/gdkprivate-wayland.h +++ b/gdk/wayland/gdkprivate-wayland.h @@ -237,10 +237,15 @@ 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_set_offer (GdkDisplay *display, + GdkAtom selection, struct wl_data_offer *wl_offer); -struct wl_data_offer * gdk_wayland_selection_get_offer (GdkDisplay *display); -GList * gdk_wayland_selection_get_targets (GdkDisplay *display); +struct wl_data_offer * gdk_wayland_selection_get_offer (GdkDisplay *display, + GdkAtom selection); +GList * gdk_wayland_selection_get_targets (GdkDisplay *display, + GdkAtom selection); void gdk_wayland_selection_store (GdkWindow *window, GdkAtom type, diff --git a/gdk/wayland/gdkselection-wayland.c b/gdk/wayland/gdkselection-wayland.c index f9f3f1111c..455b0e6637 100644 --- a/gdk/wayland/gdkselection-wayland.c +++ b/gdk/wayland/gdkselection-wayland.c @@ -36,6 +36,7 @@ typedef struct _SelectionBuffer SelectionBuffer; typedef struct _StoredSelection StoredSelection; typedef struct _AsyncWriteData AsyncWriteData; +typedef struct _DataOfferData DataOfferData; struct _SelectionBuffer { @@ -64,6 +65,13 @@ struct _DataSourceData GdkAtom selection; }; +struct _DataOfferData +{ + struct wl_data_offer *offer; + GList *targets; /* List of GdkAtom */ + GdkAtom requested_target; +}; + struct _AsyncWriteData { GOutputStream *stream; @@ -81,11 +89,10 @@ static GdkAtom atoms[2] = { 0 }; struct _GdkWaylandSelection { /* Destination-side data */ - struct wl_data_offer *offer; - GdkAtom source_requested_target; - + DataOfferData *dnd_offer; + DataOfferData *clipboard_offer; + GHashTable *offers; /* Currently alive offers, Hashtable of wl_data_offer->DataOfferData */ GHashTable *selection_buffers; /* Hashtable of target_atom->SelectionBuffer */ - GList *targets; /* List of GdkAtom */ /* Source-side data */ StoredSelection stored_selection; @@ -260,6 +267,25 @@ selection_buffer_read (SelectionBuffer *buffer) buffer); } +static DataOfferData * +data_offer_data_new (struct wl_data_offer *offer) +{ + DataOfferData *info; + + info = g_slice_new0 (DataOfferData); + info->offer = offer; + + return info; +} + +static void +data_offer_data_free (DataOfferData *info) +{ + wl_data_offer_destroy (info->offer); + g_list_free (info->targets); + g_slice_free (DataOfferData, info); +} + GdkWaylandSelection * gdk_wayland_selection_new (void) { @@ -271,8 +297,11 @@ gdk_wayland_selection_new (void) selection = g_new0 (GdkWaylandSelection, 1); selection->selection_buffers = - g_hash_table_new_full (NULL, NULL, NULL, - (GDestroyNotify) selection_buffer_cancel_and_unref); + g_hash_table_new_full (NULL, NULL, NULL, + (GDestroyNotify) selection_buffer_cancel_and_unref); + selection->offers = + g_hash_table_new_full (NULL, NULL, NULL, + (GDestroyNotify) data_offer_data_free); selection->stored_selection.fd = -1; selection->source_targets = g_array_new (FALSE, FALSE, sizeof (GdkAtom)); return selection; @@ -284,9 +313,7 @@ gdk_wayland_selection_free (GdkWaylandSelection *selection) g_hash_table_destroy (selection->selection_buffers); g_array_unref (selection->source_targets); - if (selection->targets) - g_list_free (selection->targets); - + g_hash_table_destroy (selection->offers); g_free (selection->stored_selection.data); if (selection->stored_selection.cancellable) @@ -298,8 +325,6 @@ gdk_wayland_selection_free (GdkWaylandSelection *selection) if (selection->stored_selection.fd > 0) close (selection->stored_selection.fd); - if (selection->offer) - wl_data_offer_destroy (selection->offer); if (selection->clipboard_source) wl_data_source_destroy (selection->clipboard_source); if (selection->dnd_source) @@ -314,57 +339,105 @@ data_offer_offer (void *data, const char *type) { GdkWaylandSelection *selection = data; + DataOfferData *info; GdkAtom atom = gdk_atom_intern (type, FALSE); - if (g_list_find (selection->targets, atom)) + info = g_hash_table_lookup (selection->offers, wl_data_offer); + + if (!info || g_list_find (info->targets, atom)) return; - selection->targets = g_list_prepend (selection->targets, atom); + info->targets = g_list_prepend (info->targets, atom); } static const struct wl_data_offer_listener data_offer_listener = { data_offer_offer, }; +DataOfferData * +selection_lookup_offer_by_atom (GdkWaylandSelection *selection, + GdkAtom selection_atom) +{ + if (selection_atom == atoms[ATOM_CLIPBOARD]) + return selection->clipboard_offer; + else if (selection_atom == atoms[ATOM_DND]) + return selection->dnd_offer; + else + return NULL; +} + +void +gdk_wayland_selection_ensure_offer (GdkDisplay *display, + struct wl_data_offer *wl_offer) +{ + GdkWaylandSelection *selection = gdk_wayland_display_get_selection (display); + DataOfferData *info; + + info = g_hash_table_lookup (selection->offers, wl_offer); + + if (!info) + { + info = data_offer_data_new (wl_offer); + g_hash_table_insert (selection->offers, wl_offer, info); + wl_data_offer_add_listener (wl_offer, + &data_offer_listener, + selection); + } +} + void gdk_wayland_selection_set_offer (GdkDisplay *display, + GdkAtom selection_atom, struct wl_data_offer *wl_offer) { GdkWaylandSelection *selection = gdk_wayland_display_get_selection (display); + struct wl_data_offer *prev_offer; + DataOfferData *info; - if (selection->offer == wl_offer) - return; + info = g_hash_table_lookup (selection->offers, wl_offer); - if (selection->offer) - wl_data_offer_destroy (selection->offer); + prev_offer = gdk_wayland_selection_get_offer (display, selection_atom); - selection->offer = wl_offer; + if (prev_offer) + g_hash_table_remove (selection->offers, prev_offer); - if (wl_offer) - wl_data_offer_add_listener (wl_offer, - &data_offer_listener, - selection); + if (selection_atom == atoms[ATOM_CLIPBOARD]) + selection->clipboard_offer = info; + else if (selection_atom == atoms[ATOM_DND]) + selection->dnd_offer = info; /* Clear all buffers */ g_hash_table_remove_all (selection->selection_buffers); - g_list_free (selection->targets); - selection->targets = NULL; } struct wl_data_offer * -gdk_wayland_selection_get_offer (GdkDisplay *display) +gdk_wayland_selection_get_offer (GdkDisplay *display, + GdkAtom selection_atom) { GdkWaylandSelection *selection = gdk_wayland_display_get_selection (display); + const DataOfferData *info; - return selection->offer; + info = selection_lookup_offer_by_atom (selection, selection_atom); + + if (info) + return info->offer; + + return NULL; } GList * -gdk_wayland_selection_get_targets (GdkDisplay *display) +gdk_wayland_selection_get_targets (GdkDisplay *display, + GdkAtom selection_atom) { GdkWaylandSelection *selection = gdk_wayland_display_get_selection (display); + const DataOfferData *info; - return selection->targets; + info = selection_lookup_offer_by_atom (selection, selection_atom); + + if (info) + return info->targets; + + return NULL; } static void @@ -584,6 +657,7 @@ gdk_wayland_selection_request_target (GdkWaylandSelection *wayland_selection, GdkAtom target, gint fd) { + DataOfferData *offer; GdkAtom selection; if (wayland_selection->clipboard_owner == window) @@ -593,8 +667,10 @@ gdk_wayland_selection_request_target (GdkWaylandSelection *wayland_selection, else return FALSE; + offer = selection_lookup_offer_by_atom (wayland_selection, selection); + if (wayland_selection->stored_selection.fd == fd && - wayland_selection->source_requested_target == target) + offer->requested_target == target) return FALSE; /* If we didn't issue gdk_wayland_selection_check_write() yet @@ -605,8 +681,7 @@ gdk_wayland_selection_request_target (GdkWaylandSelection *wayland_selection, close (wayland_selection->stored_selection.fd); wayland_selection->stored_selection.fd = fd; - - wayland_selection->source_requested_target = target; + offer->requested_target = target; if (window && gdk_wayland_selection_source_handles_target (wayland_selection, target)) @@ -707,8 +782,6 @@ data_source_send (void *data, _gdk_wayland_drag_context_emit_event (context, GDK_DROP_FINISHED, GDK_CURRENT_TIME); } - - wayland_selection->source_requested_target = GDK_NONE; } static void @@ -931,9 +1004,14 @@ _gdk_wayland_display_convert_selection (GdkDisplay *display, { GdkWaylandSelection *wayland_selection = gdk_wayland_display_get_selection (display); SelectionBuffer *buffer_data; + struct wl_data_offer *offer; gchar *mimetype; + GList *target_list; + + offer = gdk_wayland_selection_get_offer (display, selection); + target_list = gdk_wayland_selection_get_targets (display, selection); - if (!wayland_selection->offer) + if (!offer) { GdkEvent *event; @@ -954,7 +1032,7 @@ _gdk_wayland_display_convert_selection (GdkDisplay *display, mimetype = gdk_atom_name (target); if (target != gdk_atom_intern_static_string ("TARGETS")) - wl_data_offer_accept (wayland_selection->offer, + wl_data_offer_accept (offer, _gdk_wayland_display_get_serial (GDK_WAYLAND_DISPLAY (display)), mimetype); @@ -974,17 +1052,16 @@ _gdk_wayland_display_convert_selection (GdkDisplay *display, gint i = 0; GList *l; - natoms = g_list_length (wayland_selection->targets); + natoms = g_list_length (target_list); targets = g_new0 (GdkAtom, natoms); - for (l = wayland_selection->targets; l; l = l->next) + for (l = target_list; l; l = l->next) targets[i++] = l->data; } else { g_unix_open_pipe (pipe_fd, FD_CLOEXEC, NULL); - wl_data_offer_receive (wayland_selection->offer, - mimetype, pipe_fd[1]); + wl_data_offer_receive (offer, mimetype, pipe_fd[1]); stream = g_unix_input_stream_new (pipe_fd[0], TRUE); close (pipe_fd[1]); } -- cgit v1.2.1