From 297d11b323253aad288dd1ad3d1f3f4556cd08e1 Mon Sep 17 00:00:00 2001 From: Carlos Garnacho Date: Wed, 3 Feb 2016 18:39:58 +0100 Subject: [PATCH] WIP: Implement the primary selection protocol --- src/Makefile.am | 2 + .../meta-wayland-data-device-private.h | 6 + src/wayland/meta-wayland-data-device.c | 307 +++++++++++++++++- src/wayland/meta-wayland-data-device.h | 5 + src/wayland/meta-wayland-pointer.c | 3 + src/wayland/meta-xwayland-selection.c | 16 +- 6 files changed, 328 insertions(+), 11 deletions(-) diff --git a/src/Makefile.am b/src/Makefile.am index 8dedc81f9..9e2673833 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -45,6 +45,8 @@ mutter_built_sources = \ if HAVE_WAYLAND mutter_built_sources += \ + primary-selection-unstable-v1-protocol.c \ + primary-selection-unstable-v1-server-protocol.h \ pointer-gestures-unstable-v1-protocol.c \ pointer-gestures-unstable-v1-server-protocol.h \ gtk-shell-protocol.c \ diff --git a/src/wayland/meta-wayland-data-device-private.h b/src/wayland/meta-wayland-data-device-private.h index 9f5377af7..5e651300d 100644 --- a/src/wayland/meta-wayland-data-device-private.h +++ b/src/wayland/meta-wayland-data-device-private.h @@ -31,4 +31,10 @@ G_DECLARE_FINAL_TYPE (MetaWaylandDataSourceWayland, META, WAYLAND_DATA_SOURCE_WAYLAND, MetaWaylandDataSource); +#define META_TYPE_WAYLAND_DATA_SOURCE_PRIMARY (meta_wayland_data_source_primary_get_type ()) +G_DECLARE_FINAL_TYPE (MetaWaylandDataSourcePrimary, + meta_wayland_data_source_primary, + META, WAYLAND_DATA_SOURCE_PRIMARY, + MetaWaylandDataSourceWayland); + #endif /* META_WAYLAND_DATA_DEVICE_PRIVATE_H */ diff --git a/src/wayland/meta-wayland-data-device.c b/src/wayland/meta-wayland-data-device.c index b0652a02e..1d52c0d47 100644 --- a/src/wayland/meta-wayland-data-device.c +++ b/src/wayland/meta-wayland-data-device.c @@ -37,6 +37,8 @@ #include "meta-wayland-private.h" #include "meta-dnd-actor-private.h" +#include "primary-selection-unstable-v1-server-protocol.h" + #define ALL_ACTIONS (WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY | \ WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE | \ WL_DATA_DEVICE_MANAGER_DND_ACTION_ASK) @@ -70,13 +72,22 @@ typedef struct _MetaWaylandDataSourceWayland struct wl_resource *resource; } MetaWaylandDataSourceWayland; +typedef struct _MetaWaylandDataSourcePrimary +{ + MetaWaylandDataSourceWayland parent; +} MetaWaylandDataSourcePrimary; + G_DEFINE_TYPE_WITH_PRIVATE (MetaWaylandDataSource, meta_wayland_data_source, G_TYPE_OBJECT); G_DEFINE_TYPE (MetaWaylandDataSourceWayland, meta_wayland_data_source_wayland, META_TYPE_WAYLAND_DATA_SOURCE); +G_DEFINE_TYPE (MetaWaylandDataSourcePrimary, meta_wayland_data_source_primary, + META_TYPE_WAYLAND_DATA_SOURCE_WAYLAND); static MetaWaylandDataSource * meta_wayland_data_source_wayland_new (struct wl_resource *resource); +static MetaWaylandDataSource * +meta_wayland_data_source_primary_new (struct wl_resource *resource); static void drag_grab_data_source_destroyed (gpointer data, GObject *where_the_object_was); @@ -160,7 +171,8 @@ static void meta_wayland_data_source_target (MetaWaylandDataSource *source, const char *mime_type) { - META_WAYLAND_DATA_SOURCE_GET_CLASS (source)->target (source, mime_type); + if (META_WAYLAND_DATA_SOURCE_GET_CLASS (source)->target) + META_WAYLAND_DATA_SOURCE_GET_CLASS (source)->target (source, mime_type); } void @@ -430,6 +442,32 @@ static const struct wl_data_offer_interface data_offer_interface = { data_offer_set_actions, }; +static void +primary_offer_receive (struct wl_client *client, struct wl_resource *resource, + const char *mime_type, int32_t fd, uint32_t serial) +{ + MetaWaylandDataOffer *offer = wl_resource_get_user_data (resource); + MetaWaylandDataSource *source = offer->source; + MetaWaylandSeat *seat; + + if (!source) + { + close (fd); + return; + } + + seat = meta_wayland_data_source_get_seat (source); + + if (serial == seat->keyboard.key_serial || + serial == seat->pointer.click_serial) + meta_wayland_data_source_send (source, mime_type, fd); +} + +static const struct zwp_primary_selection_offer_v1_interface primary_offer_interface = { + primary_offer_receive, + data_offer_destroy, +}; + static void meta_wayland_data_source_notify_drop_performed (MetaWaylandDataSource *source) { @@ -500,6 +538,35 @@ meta_wayland_data_source_send_offer (MetaWaylandDataSource *source, return offer->resource; } +static struct wl_resource * +meta_wayland_data_source_send_primary_offer (MetaWaylandDataSource *source, + struct wl_resource *target) +{ + MetaWaylandDataSourcePrivate *priv = + meta_wayland_data_source_get_instance_private (source); + MetaWaylandDataOffer *offer = g_slice_new0 (MetaWaylandDataOffer); + char **p; + + offer->source = source; + g_object_add_weak_pointer (G_OBJECT (source), (gpointer *)&offer->source); + offer->resource = wl_resource_create (wl_resource_get_client (target), + &zwp_primary_selection_offer_v1_interface, + wl_resource_get_version (target), 0); + wl_resource_set_implementation (offer->resource, + &primary_offer_interface, + offer, + destroy_data_offer); + + zwp_primary_selection_device_v1_send_data_offer (target, offer->resource); + + wl_array_for_each (p, &priv->mime_types) + zwp_primary_selection_offer_v1_send_offer (offer->resource, *p); + + meta_wayland_data_source_set_current_offer (source, offer); + + return offer->resource; +} + static void data_source_offer (struct wl_client *client, struct wl_resource *resource, const char *type) @@ -561,6 +628,11 @@ static struct wl_data_source_interface data_source_interface = { data_source_set_actions }; +static struct zwp_primary_selection_source_v1_interface primary_source_interface = { + data_source_offer, + data_source_destroy, +}; + struct _MetaWaylandDragGrab { MetaWaylandPointerGrab generic; @@ -1108,6 +1180,43 @@ meta_wayland_data_source_wayland_class_init (MetaWaylandDataSourceWaylandClass * data_source_class->drag_finished = meta_wayland_source_drag_finished; } +static void +meta_wayland_data_source_primary_send (MetaWaylandDataSource *source, + const gchar *mime_type, + gint fd) +{ + MetaWaylandDataSourceWayland *source_wayland; + + source_wayland = (MetaWaylandDataSourceWayland *) source; + zwp_primary_selection_source_v1_send_send (source_wayland->resource, + mime_type, fd); +} + +static void +meta_wayland_data_source_primary_cancel (MetaWaylandDataSource *source) +{ + MetaWaylandDataSourceWayland *source_wayland; + + source_wayland = (MetaWaylandDataSourceWayland *) source; + zwp_primary_selection_source_v1_send_cancelled (source_wayland->resource); +} + +static void +meta_wayland_data_source_primary_init (MetaWaylandDataSourcePrimary *source_primary) +{ +} + +static void +meta_wayland_data_source_primary_class_init (MetaWaylandDataSourcePrimaryClass *klass) +{ + MetaWaylandDataSourceClass *data_source_class = + META_WAYLAND_DATA_SOURCE_CLASS (klass); + + data_source_class->send = meta_wayland_data_source_primary_send; + data_source_class->cancel = meta_wayland_data_source_primary_cancel; + data_source_class->target = NULL; +} + static void meta_wayland_data_source_finalize (GObject *object) { @@ -1299,6 +1408,7 @@ meta_wayland_data_device_set_selection (MetaWaylandDataDevice *data_device, if (source) { + meta_wayland_data_source_set_seat (source, seat); g_object_weak_ref (G_OBJECT (source), selection_data_source_destroyed, data_device); @@ -1351,6 +1461,93 @@ static const struct wl_data_device_interface data_device_interface = { data_device_release, }; +static void +primary_source_destroyed (gpointer data, + GObject *object_was_here) +{ + MetaWaylandDataDevice *data_device = data; + MetaWaylandSeat *seat = wl_container_of (data_device, seat, data_device); + struct wl_client *focus_client = NULL; + + data_device->primary_data_source = NULL; + + focus_client = meta_wayland_keyboard_get_focus_client (&seat->keyboard); + if (focus_client) + { + struct wl_resource *data_device_resource; + + data_device_resource = wl_resource_find_for_client (&data_device->primary_resource_list, focus_client); + if (data_device_resource) + zwp_primary_selection_device_v1_send_selection (data_device_resource, NULL); + } +} + +void +meta_wayland_data_device_set_primary (MetaWaylandDataDevice *data_device, + MetaWaylandDataSource *source) +{ + MetaWaylandSeat *seat = wl_container_of (data_device, seat, data_device); + struct wl_resource *data_device_resource, *offer; + struct wl_client *focus_client; + + if (data_device->primary_data_source) + { + meta_wayland_data_source_cancel (data_device->primary_data_source); + g_object_weak_unref (G_OBJECT (data_device->primary_data_source), + primary_source_destroyed, + data_device); + data_device->primary_data_source = NULL; + } + + data_device->primary_data_source = source; + + focus_client = meta_wayland_keyboard_get_focus_client (&seat->keyboard); + if (focus_client) + { + data_device_resource = wl_resource_find_for_client (&data_device->primary_resource_list, focus_client); + if (data_device_resource) + { + if (data_device->primary_data_source) + { + offer = meta_wayland_data_source_send_primary_offer (data_device->primary_data_source, + data_device_resource); + zwp_primary_selection_device_v1_send_selection (data_device_resource, offer); + } + else + { + zwp_primary_selection_device_v1_send_selection (data_device_resource, NULL); + } + } + } + + if (source) + { + meta_wayland_data_source_set_seat (source, seat); + g_object_weak_ref (G_OBJECT (source), + primary_source_destroyed, + data_device); + } + + wl_signal_emit (&data_device->primary_ownership_signal, source); +} + +static void +primary_device_set_selection (struct wl_client *client, + struct wl_resource *resource, + struct wl_resource *source_resource) +{ + MetaWaylandDataDevice *data_device = wl_resource_get_user_data (resource); + MetaWaylandDataSource *source; + + source = wl_resource_get_user_data (source_resource); + meta_wayland_data_device_set_primary (data_device, source); +} + +static const struct zwp_primary_selection_device_v1_interface primary_device_interface = { + primary_device_set_selection, + data_device_release, +}; + static void destroy_data_source (struct wl_resource *resource) { @@ -1389,6 +1586,48 @@ static const struct wl_data_device_manager_interface manager_interface = { get_data_device }; +static void +primary_device_manager_create_source (struct wl_client *client, + struct wl_resource *manager_resource, + guint32 id) +{ + struct wl_resource *source_resource; + + source_resource = + wl_resource_create (client, &zwp_primary_selection_source_v1_interface, + wl_resource_get_version (manager_resource), + id); + meta_wayland_data_source_primary_new (source_resource); +} + +static void +primary_device_manager_get_device (struct wl_client *client, + struct wl_resource *manager_resource, + guint32 id, + struct wl_resource *seat_resource) +{ + MetaWaylandSeat *seat = wl_resource_get_user_data (seat_resource); + struct wl_resource *cr; + + cr = wl_resource_create (client, &zwp_primary_selection_device_v1_interface, + wl_resource_get_version (manager_resource), id); + wl_resource_set_implementation (cr, &primary_device_interface, + &seat->data_device, unbind_resource); + wl_list_insert (&seat->data_device.primary_resource_list, wl_resource_get_link (cr)); +} + +static void +primary_device_manager_destroy (struct wl_client *client, + struct wl_resource *manager_resource) +{ +} + +static const struct zwp_primary_selection_device_manager_v1_interface primary_manager_interface = { + primary_device_manager_create_source, + primary_device_manager_get_device, + primary_device_manager_destroy +}; + static void bind_manager (struct wl_client *client, void *data, guint32 version, guint32 id) @@ -1398,6 +1637,19 @@ bind_manager (struct wl_client *client, wl_resource_set_implementation (resource, &manager_interface, NULL, NULL); } +static void +bind_primary_manager (struct wl_client *client, + void *data, + uint32_t version, + uint32_t id) +{ + struct wl_resource *resource; + + resource = wl_resource_create (client, &zwp_primary_selection_device_manager_v1_interface, + version, id); + wl_resource_set_implementation (resource, &primary_manager_interface, NULL, NULL); +} + void meta_wayland_data_device_manager_init (MetaWaylandCompositor *compositor) { @@ -1406,13 +1658,20 @@ meta_wayland_data_device_manager_init (MetaWaylandCompositor *compositor) META_WL_DATA_DEVICE_MANAGER_VERSION, NULL, bind_manager) == NULL) g_error ("Could not create data_device"); + + if (wl_global_create (compositor->wayland_display, + &zwp_primary_selection_device_manager_v1_interface, + 1, NULL, bind_primary_manager) == NULL) + g_error ("Could not create data_device"); } void meta_wayland_data_device_init (MetaWaylandDataDevice *data_device) { wl_list_init (&data_device->resource_list); + wl_list_init (&data_device->primary_resource_list); wl_signal_init (&data_device->selection_ownership_signal); + wl_signal_init (&data_device->primary_ownership_signal); wl_signal_init (&data_device->dnd_ownership_signal); } @@ -1435,17 +1694,32 @@ meta_wayland_data_device_set_keyboard_focus (MetaWaylandDataDevice *data_device) return; data_device_resource = wl_resource_find_for_client (&data_device->resource_list, focus_client); - if (!data_device_resource) - return; - - source = data_device->selection_data_source; - if (source) + if (data_device_resource) { - offer = meta_wayland_data_source_send_offer (source, data_device_resource); - wl_data_device_send_selection (data_device_resource, offer); + source = data_device->selection_data_source; + if (source) + { + offer = meta_wayland_data_source_send_offer (source, data_device_resource); + wl_data_device_send_selection (data_device_resource, offer); + } + else + wl_data_device_send_selection (data_device_resource, NULL); + } + + data_device_resource = wl_resource_find_for_client (&data_device->primary_resource_list, focus_client); + if (data_device_resource) + { + source = data_device->primary_data_source; + if (source) + { + offer = meta_wayland_data_source_send_primary_offer (source, data_device_resource); + zwp_primary_selection_device_v1_send_selection (data_device_resource, offer); + } + else + { + zwp_primary_selection_device_v1_send_selection (data_device_resource, NULL); + } } - else - wl_data_device_send_selection (data_device_resource, NULL); } gboolean @@ -1486,6 +1760,19 @@ meta_wayland_data_source_wayland_new (struct wl_resource *resource) return META_WAYLAND_DATA_SOURCE (source_wayland); } +static MetaWaylandDataSource * +meta_wayland_data_source_primary_new (struct wl_resource *resource) +{ + MetaWaylandDataSourceWayland *source_wayland = + g_object_new (META_TYPE_WAYLAND_DATA_SOURCE_PRIMARY, NULL); + + source_wayland->resource = resource; + wl_resource_set_implementation (resource, &primary_source_interface, + source_wayland, destroy_data_source); + + return META_WAYLAND_DATA_SOURCE (source_wayland); +} + gboolean meta_wayland_data_source_add_mime_type (MetaWaylandDataSource *source, const gchar *mime_type) diff --git a/src/wayland/meta-wayland-data-device.h b/src/wayland/meta-wayland-data-device.h index cdb4f05d7..d7795022d 100644 --- a/src/wayland/meta-wayland-data-device.h +++ b/src/wayland/meta-wayland-data-device.h @@ -57,13 +57,16 @@ struct _MetaWaylandDataDevice uint32_t selection_serial; MetaWaylandDataSource *selection_data_source; MetaWaylandDataSource *dnd_data_source; + MetaWaylandDataSource *primary_data_source; struct wl_listener selection_data_source_listener; struct wl_list resource_list; + struct wl_list primary_resource_list; MetaWaylandDragGrab *current_grab; struct wl_client *focus_client; struct wl_signal selection_ownership_signal; struct wl_signal dnd_ownership_signal; + struct wl_signal primary_ownership_signal; }; void meta_wayland_data_device_manager_init (MetaWaylandCompositor *compositor); @@ -80,6 +83,8 @@ void meta_wayland_data_device_set_dnd_source (MetaWaylandDataDevice *data_de void meta_wayland_data_device_set_selection (MetaWaylandDataDevice *data_device, MetaWaylandDataSource *source, guint32 serial); +void meta_wayland_data_device_set_primary (MetaWaylandDataDevice *data_device, + MetaWaylandDataSource *source); gboolean meta_wayland_data_source_add_mime_type (MetaWaylandDataSource *source, const gchar *mime_type); diff --git a/src/wayland/meta-wayland-pointer.c b/src/wayland/meta-wayland-pointer.c index 4fb32bbdf..c152bf9e1 100644 --- a/src/wayland/meta-wayland-pointer.c +++ b/src/wayland/meta-wayland-pointer.c @@ -400,6 +400,9 @@ meta_wayland_pointer_send_button (MetaWaylandPointer *pointer, time = clutter_event_get_time (event); serial = wl_display_next_serial (display); + if (event->type == CLUTTER_BUTTON_PRESS) + pointer->click_serial = serial; + wl_resource_for_each (resource, &pointer->focus_client->pointer_resources) { wl_pointer_send_button (resource, serial, diff --git a/src/wayland/meta-xwayland-selection.c b/src/wayland/meta-xwayland-selection.c index 5d3405ad8..cbafbef34 100644 --- a/src/wayland/meta-xwayland-selection.c +++ b/src/wayland/meta-xwayland-selection.c @@ -91,6 +91,7 @@ struct _MetaWaylandDataSourceXWayland struct _MetaXWaylandSelection { MetaSelectionBridge clipboard; + MetaSelectionBridge primary; MetaDndBridge dnd; }; @@ -396,6 +397,8 @@ atom_to_selection_bridge (MetaWaylandCompositor *compositor, if (selection_atom == selection_data->clipboard.selection_atom) return &selection_data->clipboard; + else if (selection_atom == selection_data->primary.selection_atom) + return &selection_data->primary; else if (selection_atom == selection_data->dnd.selection.selection_atom) return &selection_data->dnd.selection; else @@ -530,6 +533,8 @@ data_device_get_active_source_for_atom (MetaWaylandDataDevice *data_device, { if (selection_atom == gdk_x11_get_xatom_by_name ("CLIPBOARD")) return data_device->selection_data_source; + else if (selection_atom == gdk_x11_get_xatom_by_name ("PRIMARY")) + return data_device->primary_data_source; else if (selection_atom == xdnd_atoms[ATOM_DND_SELECTION]) return data_device->dnd_data_source; else @@ -1058,6 +1063,10 @@ meta_xwayland_selection_get_x11_targets (MetaWaylandCompositor *compositor, meta_wayland_data_device_set_selection (&compositor->seat->data_device, data_source, wl_display_next_serial (compositor->wayland_display)); } + else if (selection->selection_atom == gdk_x11_get_xatom_by_name ("PRIMARY")) + { + meta_wayland_data_device_set_primary (&compositor->seat->data_device, data_source); + } } else g_object_unref (data_source); @@ -1529,7 +1538,8 @@ meta_xwayland_selection_handle_xfixes_selection_notify (MetaWaylandCompositor *c if (!selection) return FALSE; - if (selection->selection_atom == gdk_x11_get_xatom_by_name ("CLIPBOARD")) + if (selection->selection_atom == gdk_x11_get_xatom_by_name ("CLIPBOARD") || + selection->selection_atom == gdk_x11_get_xatom_by_name ("PRIMARY")) { if (event->owner == None) { @@ -1712,6 +1722,9 @@ meta_xwayland_init_selection (void) init_selection_bridge (&manager->selection_data->clipboard, gdk_x11_get_xatom_by_name ("CLIPBOARD"), &compositor->seat->data_device.selection_ownership_signal); + init_selection_bridge (&manager->selection_data->primary, + gdk_x11_get_xatom_by_name ("PRIMARY"), + &compositor->seat->data_device.primary_ownership_signal); init_selection_bridge (&manager->selection_data->dnd.selection, xdnd_atoms[ATOM_DND_SELECTION], &compositor->seat->data_device.dnd_ownership_signal); @@ -1730,6 +1743,7 @@ meta_xwayland_shutdown_selection (void) meta_xwayland_shutdown_dnd (manager); shutdown_selection_bridge (&selection->clipboard); + shutdown_selection_bridge (&selection->primary); shutdown_selection_bridge (&selection->dnd.selection); g_slice_free (MetaXWaylandSelection, selection);