wayland: Separate selection buffers and other per-selection atom data

This has most notably impact in selection buffers, because those were
shared across all selection atoms. This turned out wrong on 2 situations:
- Because the selection atom was set at SelectionBuffer creation time, the
  GDK_SELECTION_NOTIFY events generated will have unexpected info if the
  buffer is attempted to be reused for another selection.
- Anytime different selections imply different stored content for the same
  target.

This is better separated into per-selection buffers, so it's not possible
to get collisions if a same target is used across different selections.

https://bugzilla.gnome.org/show_bug.cgi?id=768177
This commit is contained in:
Carlos Garnacho 2016-06-29 17:13:55 +02:00
parent 4b003a75aa
commit 0d30ad279f

View File

@ -35,6 +35,7 @@
#include <string.h> #include <string.h>
typedef struct _SelectionBuffer SelectionBuffer; typedef struct _SelectionBuffer SelectionBuffer;
typedef struct _SelectionData SelectionData;
typedef struct _StoredSelection StoredSelection; typedef struct _StoredSelection StoredSelection;
typedef struct _AsyncWriteData AsyncWriteData; typedef struct _AsyncWriteData AsyncWriteData;
typedef struct _DataOfferData DataOfferData; typedef struct _DataOfferData DataOfferData;
@ -80,22 +81,26 @@ struct _AsyncWriteData
gsize index; gsize index;
}; };
struct _SelectionData
{
DataOfferData *offer;
GHashTable *buffers; /* Hashtable of target_atom->SelectionBuffer */
};
enum { enum {
ATOM_PRIMARY, ATOM_PRIMARY,
ATOM_CLIPBOARD, ATOM_CLIPBOARD,
ATOM_DND ATOM_DND,
N_ATOMS
}; };
static GdkAtom atoms[3] = { 0 }; static GdkAtom atoms[N_ATOMS] = { 0 };
struct _GdkWaylandSelection struct _GdkWaylandSelection
{ {
/* Destination-side data */ /* Destination-side data */
DataOfferData *dnd_offer; SelectionData selections[N_ATOMS];
DataOfferData *clipboard_offer;
DataOfferData *primary_offer;
GHashTable *offers; /* Currently alive offers, Hashtable of wl_data_offer->DataOfferData */ GHashTable *offers; /* Currently alive offers, Hashtable of wl_data_offer->DataOfferData */
GHashTable *selection_buffers; /* Hashtable of target_atom->SelectionBuffer */
/* Source-side data */ /* Source-side data */
StoredSelection stored_selection; StoredSelection stored_selection;
@ -307,6 +312,7 @@ GdkWaylandSelection *
gdk_wayland_selection_new (void) gdk_wayland_selection_new (void)
{ {
GdkWaylandSelection *selection; GdkWaylandSelection *selection;
gint i;
/* init atoms */ /* init atoms */
atoms[ATOM_PRIMARY] = gdk_atom_intern_static_string ("PRIMARY"); atoms[ATOM_PRIMARY] = gdk_atom_intern_static_string ("PRIMARY");
@ -314,9 +320,13 @@ gdk_wayland_selection_new (void)
atoms[ATOM_DND] = gdk_atom_intern_static_string ("GdkWaylandSelection"); atoms[ATOM_DND] = gdk_atom_intern_static_string ("GdkWaylandSelection");
selection = g_new0 (GdkWaylandSelection, 1); selection = g_new0 (GdkWaylandSelection, 1);
selection->selection_buffers = for (i = 0; i < G_N_ELEMENTS (selection->selections); i++)
g_hash_table_new_full (NULL, NULL, NULL, {
(GDestroyNotify) selection_buffer_cancel_and_unref); selection->selections[i].buffers =
g_hash_table_new_full (NULL, NULL, NULL,
(GDestroyNotify) selection_buffer_cancel_and_unref);
}
selection->offers = selection->offers =
g_hash_table_new_full (NULL, NULL, NULL, g_hash_table_new_full (NULL, NULL, NULL,
(GDestroyNotify) data_offer_data_free); (GDestroyNotify) data_offer_data_free);
@ -328,7 +338,11 @@ gdk_wayland_selection_new (void)
void void
gdk_wayland_selection_free (GdkWaylandSelection *selection) gdk_wayland_selection_free (GdkWaylandSelection *selection)
{ {
g_hash_table_destroy (selection->selection_buffers); gint i;
for (i = 0; i < G_N_ELEMENTS (selection->selections); i++)
g_hash_table_destroy (selection->selections[i].buffers);
g_array_unref (selection->source_targets); g_array_unref (selection->source_targets);
g_hash_table_destroy (selection->offers); g_hash_table_destroy (selection->offers);
@ -456,16 +470,16 @@ static const struct gtk_primary_selection_offer_listener primary_offer_listener
primary_offer_offer, primary_offer_offer,
}; };
DataOfferData * SelectionData *
selection_lookup_offer_by_atom (GdkWaylandSelection *selection, selection_lookup_offer_by_atom (GdkWaylandSelection *selection,
GdkAtom selection_atom) GdkAtom selection_atom)
{ {
if (selection_atom == atoms[ATOM_PRIMARY]) if (selection_atom == atoms[ATOM_PRIMARY])
return selection->primary_offer; return &selection->selections[ATOM_PRIMARY];
else if (selection_atom == atoms[ATOM_CLIPBOARD]) else if (selection_atom == atoms[ATOM_CLIPBOARD])
return selection->clipboard_offer; return &selection->selections[ATOM_CLIPBOARD];
else if (selection_atom == atoms[ATOM_DND]) else if (selection_atom == atoms[ATOM_DND])
return selection->dnd_offer; return &selection->selections[ATOM_DND];
else else
return NULL; return NULL;
} }
@ -517,6 +531,7 @@ gdk_wayland_selection_set_offer (GdkDisplay *display,
{ {
GdkWaylandSelection *selection = gdk_wayland_display_get_selection (display); GdkWaylandSelection *selection = gdk_wayland_display_get_selection (display);
struct wl_data_offer *prev_offer; struct wl_data_offer *prev_offer;
SelectionData *selection_data;
DataOfferData *info; DataOfferData *info;
info = g_hash_table_lookup (selection->offers, wl_offer); info = g_hash_table_lookup (selection->offers, wl_offer);
@ -526,15 +541,14 @@ gdk_wayland_selection_set_offer (GdkDisplay *display,
if (prev_offer) if (prev_offer)
g_hash_table_remove (selection->offers, prev_offer); g_hash_table_remove (selection->offers, prev_offer);
if (selection_atom == atoms[ATOM_PRIMARY]) selection_data = selection_lookup_offer_by_atom (selection, selection_atom);
selection->primary_offer = info;
else if (selection_atom == atoms[ATOM_CLIPBOARD])
selection->clipboard_offer = info;
else if (selection_atom == atoms[ATOM_DND])
selection->dnd_offer = info;
/* Clear all buffers */ if (selection_data)
g_hash_table_remove_all (selection->selection_buffers); {
selection_data->offer = info;
/* Clear all buffers */
g_hash_table_remove_all (selection_data->buffers);
}
} }
gpointer gpointer
@ -542,12 +556,12 @@ gdk_wayland_selection_get_offer (GdkDisplay *display,
GdkAtom selection_atom) GdkAtom selection_atom)
{ {
GdkWaylandSelection *selection = gdk_wayland_display_get_selection (display); GdkWaylandSelection *selection = gdk_wayland_display_get_selection (display);
const DataOfferData *info; const SelectionData *data;
info = selection_lookup_offer_by_atom (selection, selection_atom); data = selection_lookup_offer_by_atom (selection, selection_atom);
if (info) if (data && data->offer)
return info->offer_data; return data->offer->offer_data;
return NULL; return NULL;
} }
@ -557,12 +571,12 @@ gdk_wayland_selection_get_targets (GdkDisplay *display,
GdkAtom selection_atom) GdkAtom selection_atom)
{ {
GdkWaylandSelection *selection = gdk_wayland_display_get_selection (display); GdkWaylandSelection *selection = gdk_wayland_display_get_selection (display);
const DataOfferData *info; const SelectionData *data;
info = selection_lookup_offer_by_atom (selection, selection_atom); data = selection_lookup_offer_by_atom (selection, selection_atom);
if (info) if (data && data->offer)
return info->targets; return data->offer->targets;
return NULL; return NULL;
} }
@ -749,13 +763,17 @@ gdk_wayland_selection_lookup_requestor_buffer (GdkWindow *requestor)
GdkWaylandSelection *selection = gdk_wayland_display_get_selection (display); GdkWaylandSelection *selection = gdk_wayland_display_get_selection (display);
SelectionBuffer *buffer_data; SelectionBuffer *buffer_data;
GHashTableIter iter; GHashTableIter iter;
gint i;
g_hash_table_iter_init (&iter, selection->selection_buffers); for (i = 0; i < G_N_ELEMENTS (selection->selections); i++)
while (g_hash_table_iter_next (&iter, NULL, (gpointer*) &buffer_data))
{ {
if (g_list_find (buffer_data->requestors, requestor)) g_hash_table_iter_init (&iter, selection->selections[i].buffers);
return buffer_data;
while (g_hash_table_iter_next (&iter, NULL, (gpointer*) &buffer_data))
{
if (g_list_find (buffer_data->requestors, requestor))
return buffer_data;
}
} }
return NULL; return NULL;
@ -1255,11 +1273,16 @@ _gdk_wayland_display_convert_selection (GdkDisplay *display,
guint32 time) guint32 time)
{ {
GdkWaylandSelection *wayland_selection = gdk_wayland_display_get_selection (display); GdkWaylandSelection *wayland_selection = gdk_wayland_display_get_selection (display);
const SelectionData *selection_data;
SelectionBuffer *buffer_data; SelectionBuffer *buffer_data;
gpointer offer; gpointer offer;
gchar *mimetype; gchar *mimetype;
GList *target_list; GList *target_list;
selection_data = selection_lookup_offer_by_atom (wayland_selection, selection);
if (!selection_data)
return;
offer = gdk_wayland_selection_get_offer (display, selection); offer = gdk_wayland_selection_get_offer (display, selection);
target_list = gdk_wayland_selection_get_targets (display, selection); target_list = gdk_wayland_selection_get_targets (display, selection);
@ -1285,8 +1308,7 @@ _gdk_wayland_display_convert_selection (GdkDisplay *display,
mimetype); mimetype);
} }
buffer_data = g_hash_table_lookup (wayland_selection->selection_buffers, buffer_data = g_hash_table_lookup (selection_data->buffers, target);
target);
if (buffer_data) if (buffer_data)
selection_buffer_add_requestor (buffer_data, requestor); selection_buffer_add_requestor (buffer_data, requestor);
@ -1333,7 +1355,7 @@ _gdk_wayland_display_convert_selection (GdkDisplay *display,
g_free (targets); g_free (targets);
} }
g_hash_table_insert (wayland_selection->selection_buffers, g_hash_table_insert (selection_data->buffers,
GDK_ATOM_TO_POINTER (target), GDK_ATOM_TO_POINTER (target),
buffer_data); buffer_data);
} }