aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authoremersion <contact@emersion.fr>2018-12-07 15:50:51 +0100
committeremersion <contact@emersion.fr>2018-12-30 02:48:03 +0100
commit99d879c8878f774aac66e39d7bf4b92f3ae7f0ed (patch)
tree047d62880316a2df2f762f8387760f3c6d22f0ff
parent84c904752f10e24f052beaf12eb3cc529f1bb38d (diff)
data-control-v1: initial protocol implementation
-rw-r--r--include/wlr/types/meson.build3
-rw-r--r--include/wlr/types/wlr_data_control_v1.h47
-rw-r--r--protocol/meson.build1
-rw-r--r--protocol/wlr-data-control-unstable-v1.xml220
-rw-r--r--rootston/desktop.c11
-rw-r--r--types/meson.build1
-rw-r--r--types/wlr_data_control_v1.c413
7 files changed, 691 insertions, 5 deletions
diff --git a/include/wlr/types/meson.build b/include/wlr/types/meson.build
index 752c0dea..5e05c5e5 100644
--- a/include/wlr/types/meson.build
+++ b/include/wlr/types/meson.build
@@ -3,6 +3,7 @@ install_headers(
'wlr_buffer.h',
'wlr_compositor.h',
'wlr_cursor.h',
+ 'wlr_data_control_v1.h',
'wlr_data_device.h',
'wlr_export_dmabuf_v1.h',
'wlr_foreign_toplevel_management_v1.h',
@@ -22,8 +23,8 @@ install_headers(
'wlr_output_damage.h',
'wlr_output_layout.h',
'wlr_output.h',
- 'wlr_pointer.h',
'wlr_pointer_constraints_v1.h',
+ 'wlr_pointer.h',
'wlr_presentation_time.h',
'wlr_primary_selection.h',
'wlr_region.h',
diff --git a/include/wlr/types/wlr_data_control_v1.h b/include/wlr/types/wlr_data_control_v1.h
new file mode 100644
index 00000000..69d7281b
--- /dev/null
+++ b/include/wlr/types/wlr_data_control_v1.h
@@ -0,0 +1,47 @@
+/*
+ * This an unstable interface of wlroots. No guarantees are made regarding the
+ * future consistency of this API.
+ */
+#ifndef WLR_USE_UNSTABLE
+#error "Add -DWLR_USE_UNSTABLE to enable unstable wlroots features"
+#endif
+
+#ifndef WLR_TYPES_WLR_DATA_CONTROL_V1_H
+#define WLR_TYPES_WLR_DATA_CONTROL_V1_H
+
+#include <wayland-server.h>
+#include <wlr/types/wlr_seat.h>
+
+struct wlr_data_control_manager_v1 {
+ struct wl_global *global;
+ struct wl_list resources; // wl_resource_get_link
+ struct wl_list controls; // wlr_data_control_v1::link
+
+ struct {
+ struct wl_signal destroy;
+ struct wl_signal new_control; // wlr_data_control_v1
+ } events;
+
+ struct wl_listener display_destroy;
+};
+
+struct wlr_data_control_v1 {
+ struct wl_resource *resource;
+ struct wlr_data_control_manager_v1 *manager;
+ struct wl_list link; // wlr_data_control_manager_v1::controls
+
+ struct wlr_seat *seat;
+ struct wl_resource *selection_offer_resource; // current selection offer
+
+ struct wl_listener seat_destroy;
+ struct wl_listener seat_selection;
+};
+
+struct wlr_data_control_manager_v1 *wlr_data_control_manager_v1_create(
+ struct wl_display *display);
+void wlr_data_control_manager_v1_destroy(
+ struct wlr_data_control_manager_v1 *manager);
+
+void wlr_data_control_v1_destroy(struct wlr_data_control_v1 *control);
+
+#endif
diff --git a/protocol/meson.build b/protocol/meson.build
index 58f57046..afd10fb6 100644
--- a/protocol/meson.build
+++ b/protocol/meson.build
@@ -28,6 +28,7 @@ protocols = [
'server-decoration.xml',
'text-input-unstable-v3.xml',
'virtual-keyboard-unstable-v1.xml',
+ 'wlr-data-control-unstable-v1.xml',
'wlr-export-dmabuf-unstable-v1.xml',
'wlr-gamma-control-unstable-v1.xml',
'wlr-foreign-toplevel-management-unstable-v1.xml',
diff --git a/protocol/wlr-data-control-unstable-v1.xml b/protocol/wlr-data-control-unstable-v1.xml
new file mode 100644
index 00000000..c8d50d53
--- /dev/null
+++ b/protocol/wlr-data-control-unstable-v1.xml
@@ -0,0 +1,220 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<protocol name="wlr_data_control_unstable_v1">
+ <copyright>
+ Copyright © 2018 Simon Ser
+
+ Permission to use, copy, modify, distribute, and sell this
+ software and its documentation for any purpose is hereby granted
+ without fee, provided that the above copyright notice appear in
+ all copies and that both that copyright notice and this permission
+ notice appear in supporting documentation, and that the name of
+ the copyright holders not be used in advertising or publicity
+ pertaining to distribution of the software without specific,
+ written prior permission. The copyright holders make no
+ representations about the suitability of this software for any
+ purpose. It is provided "as is" without express or implied
+ warranty.
+
+ THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
+ SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
+ THIS SOFTWARE.
+ </copyright>
+
+ <description summary="control data devices">
+ This protocol allows a privileged client to control data devices. In
+ particular, the client will be able to manage the current selection and take
+ the role of a clipboard manager.
+
+ Warning! The protocol described in this file is experimental and
+ backward incompatible changes may be made. Backward compatible changes
+ may be added together with the corresponding interface version bump.
+ Backward incompatible changes are done by bumping the version number in
+ the protocol and interface names and resetting the interface version.
+ Once the protocol is to be declared stable, the 'z' prefix and the
+ version number in the protocol and interface names are removed and the
+ interface version number is reset.
+ </description>
+
+ <interface name="zwlr_data_control_manager_v1" version="1">
+ <description summary="manager to create per-seat data controls">
+ This interface is a manager that allows creating per-seat data controls.
+ </description>
+
+ <request name="create_data_source">
+ <description summary="create a new data source">
+ Create a new data source.
+ </description>
+ <arg name="id" type="new_id" interface="zwlr_data_control_source_v1"
+ summary="data source to create"/>
+ </request>
+
+ <request name="get_data_control">
+ <description summary="get a data control for a seat">
+ Create a data control that can be used to manage a seat's data device.
+ </description>
+ <arg name="id" type="new_id" interface="zwlr_data_control_v1"/>
+ <arg name="seat" type="object" interface="wl_seat"/>
+ </request>
+
+ <request name="destroy" type="destructor">
+ <description summary="destroy the manager">
+ All objects created by the manager will still remain valid, until their
+ appropriate destroy request has been called.
+ </description>
+ </request>
+ </interface>
+
+ <interface name="zwlr_data_control_v1" version="1">
+ <description summary="manage a data device for a seat">
+ This interface allows a client to manage a data device associated with a
+ seat.
+
+ When the seat is destroyed, this object becomes inert.
+ </description>
+
+ <request name="set_selection">
+ <description summary="copy data to the selection">
+ All objects created by the manager will still remain valid, until their
+ appropriate destroy request has been called.
+ </description>
+ <arg name="source" type="object" interface="zwlr_data_control_source_v1"/>
+ </request>
+
+ <request name="destroy" type="destructor">
+ <description summary="destroy this control">
+ Destroys the data control object.
+ </description>
+ </request>
+
+ <event name="data_offer">
+ <description summary="introduce a new wlr_data_control_offer">
+ The data_offer event introduces a new wlr_data_control_offer object,
+ which will subsequently be used in the wlr_data_control.selection event.
+ Immediately following the wlr_data_control.data_offer event, the new
+ data_offer object will send out wlr_data_control_offer.offer events to
+ describe the MIME types it offers.
+
+ This event replaces the previous data offer, which should be destroyed
+ by the client.
+ </description>
+ <arg name="id" type="new_id" interface="zwlr_data_control_offer_v1"/>
+ </event>
+
+ <event name="selection">
+ <description summary="introduce a new wlr_data_control_offer">
+ The selection event is sent out to notify the client of a new
+ wlr_data_control_offer for the selection for this device. The
+ wlr_data_control.data_offer and the wlr_data_control_offer.offer events
+ are sent out immediately before this event to introduce the data offer
+ object. The selection event is sent to a client when a new selection is
+ set. The wlr_data_control_offer is valid until a new
+ wlr_data_control_offer or NULL is received. The client must destroy the
+ previous selection wlr_data_control_offer, if any, upon receiving this
+ event.
+ </description>
+ <arg name="id" type="object" interface="zwlr_data_control_offer_v1"/>
+ </event>
+
+ <event name="finished">
+ <description summary="this data control is no longer valid">
+ This data control object is no longer valid and should be destroyed by
+ the client.
+ </description>
+ </event>
+ </interface>
+
+ <interface name="zwlr_data_control_source_v1" version="1">
+ <description summary="offer to transfer data">
+ The wlr_data_control_source object is the source side of a
+ wlr_data_control_offer. It is created by the source client in a data
+ transfer and provides a way to describe the offered data and a way to
+ respond to requests to transfer the data.
+ </description>
+
+ <enum name="error">
+ <entry name="invalid_offer" value="1"
+ summary="offer sent after wlr_data_control.set_selection"/>
+ </enum>
+
+ <request name="offer">
+ <description summary="add an offered MIME type">
+ This request adds a MIME type to the set of MIME types advertised to
+ targets. Can be called several times to offer multiple types.
+
+ Calling this after wlr_data_control.set_selection is a protocol error.
+ </description>
+ <arg name="mime_type" type="string"
+ summary="MIME type offered by the data source"/>
+ </request>
+
+ <request name="destroy" type="destructor">
+ <description summary="destroy this source">
+ Destroys the data source object.
+ </description>
+ </request>
+
+ <event name="send">
+ <description summary="send the data">
+ Request for data from the client. Send the data as the specified MIME
+ type over the passed file descriptor, then close it.
+ </description>
+ <arg name="mime_type" type="string" summary="MIME type for the data"/>
+ <arg name="fd" type="fd" summary="file descriptor for the data"/>
+ </event>
+
+ <event name="cancelled">
+ <description summary="selection was cancelled">
+ This data source is no longer valid. The data source has been replaced
+ by another data source.
+
+ The client should clean up and destroy this data source.
+ </description>
+ </event>
+ </interface>
+
+ <interface name="zwlr_data_control_offer_v1" version="1">
+ <description summary="offer to transfer data">
+ A wlr_data_control_offer represents a piece of data offered for transfer
+ by another client (the source client). The offer describes the different
+ MIME types that the data can be converted to and provides the mechanism
+ for transferring the data directly from the source client.
+ </description>
+
+ <request name="receive">
+ <description summary="request that the data is transferred">
+ To transfer the offered data, the client issues this request and
+ indicates the MIME type it wants to receive. The transfer happens
+ through the passed file descriptor (typically created with the pipe
+ system call). The source client writes the data in the MIME type
+ representation requested and then closes the file descriptor.
+
+ The receiving client reads from the read end of the pipe until EOF and
+ then closes its end, at which point the transfer is complete.
+
+ This request may happen multiple times for different MIME types.
+ </description>
+ <arg name="mime_type" type="string"
+ summary="MIME type desired by receiver"/>
+ <arg name="fd" type="fd" summary="file descriptor for data transfer"/>
+ </request>
+
+ <request name="destroy" type="destructor">
+ <description summary="destroy this offer">
+ Destroys the data offer object.
+ </description>
+ </request>
+
+ <event name="offer">
+ <description summary="advertise offered MIME type">
+ Sent immediately after creating the wlr_data_control_offer object.
+ One event per offered MIME type.
+ </description>
+ <arg name="mime_type" type="string" summary="offered MIME type"/>
+ </event>
+ </interface>
+</protocol>
diff --git a/rootston/desktop.c b/rootston/desktop.c
index 48e2635c..5e9e352a 100644
--- a/rootston/desktop.c
+++ b/rootston/desktop.c
@@ -7,24 +7,25 @@
#include <wlr/types/wlr_box.h>
#include <wlr/types/wlr_compositor.h>
#include <wlr/types/wlr_cursor.h>
+#include <wlr/types/wlr_data_control_v1.h>
#include <wlr/types/wlr_export_dmabuf_v1.h>
-#include <wlr/types/wlr_gamma_control.h>
#include <wlr/types/wlr_gamma_control_v1.h>
+#include <wlr/types/wlr_gamma_control.h>
+#include <wlr/types/wlr_gtk_primary_selection.h>
#include <wlr/types/wlr_idle_inhibit_v1.h>
#include <wlr/types/wlr_idle.h>
#include <wlr/types/wlr_input_inhibitor.h>
#include <wlr/types/wlr_layer_shell_v1.h>
#include <wlr/types/wlr_output_layout.h>
#include <wlr/types/wlr_pointer_constraints_v1.h>
-#include <wlr/types/wlr_gtk_primary_selection.h>
#include <wlr/types/wlr_server_decoration.h>
+#include <wlr/types/wlr_tablet_v2.h>
#include <wlr/types/wlr_wl_shell.h>
#include <wlr/types/wlr_xcursor_manager.h>
#include <wlr/types/wlr_xdg_output_v1.h>
+#include <wlr/types/wlr_xdg_output_v1.h>
#include <wlr/types/wlr_xdg_shell_v6.h>
#include <wlr/types/wlr_xdg_shell.h>
-#include <wlr/types/wlr_xdg_output_v1.h>
-#include <wlr/types/wlr_tablet_v2.h>
#include <wlr/util/log.h>
#include "rootston/layers.h"
#include "rootston/seat.h"
@@ -1082,6 +1083,8 @@ struct roots_desktop *desktop_create(struct roots_server *server,
desktop->foreign_toplevel_manager_v1 =
wlr_foreign_toplevel_manager_v1_create(server->wl_display);
+ wlr_data_control_manager_v1_create(server->wl_display);
+
return desktop;
}
diff --git a/types/meson.build b/types/meson.build
index 1813b144..299ccc9a 100644
--- a/types/meson.build
+++ b/types/meson.build
@@ -27,6 +27,7 @@ lib_wlr_types = static_library(
'wlr_buffer.c',
'wlr_compositor.c',
'wlr_cursor.c',
+ 'wlr_data_control_v1.c',
'wlr_export_dmabuf_v1.c',
'wlr_foreign_toplevel_management_v1.c',
'wlr_gamma_control_v1.c',
diff --git a/types/wlr_data_control_v1.c b/types/wlr_data_control_v1.c
new file mode 100644
index 00000000..8d90074a
--- /dev/null
+++ b/types/wlr_data_control_v1.c
@@ -0,0 +1,413 @@
+#define _POSIX_C_SOURCE 200809L
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <wlr/types/wlr_data_control_v1.h>
+#include <wlr/types/wlr_data_device.h>
+#include "util/signal.h"
+#include "wlr-data-control-unstable-v1-protocol.h"
+
+#define DATA_CONTROL_MANAGER_VERSION 1
+
+struct client_data_source {
+ struct wlr_data_source source;
+ struct wl_resource *resource;
+ bool finalized;
+};
+
+static const struct wlr_data_source_impl client_source_impl;
+
+static struct client_data_source *
+ client_data_source_from_source(struct wlr_data_source *wlr_source) {
+ assert(wlr_source->impl == &client_source_impl);
+ return (struct client_data_source *)wlr_source;
+}
+
+static void client_source_send(struct wlr_data_source *wlr_source,
+ const char *mime_type, int fd) {
+ struct client_data_source *source =
+ client_data_source_from_source(wlr_source);
+ zwlr_data_control_source_v1_send_send(source->resource, mime_type, fd);
+ close(fd);
+}
+
+static void client_source_cancel(struct wlr_data_source *wlr_source) {
+ struct client_data_source *source =
+ client_data_source_from_source(wlr_source);
+ zwlr_data_control_source_v1_send_cancelled(source->resource);
+ // Make the resource inert
+ wl_resource_set_user_data(source->resource, NULL);
+ free(source);
+}
+
+static const struct wlr_data_source_impl client_source_impl = {
+ .send = client_source_send,
+ .cancel = client_source_cancel,
+};
+
+static const struct zwlr_data_control_source_v1_interface source_impl;
+
+static struct client_data_source *source_from_resource(
+ struct wl_resource *resource) {
+ assert(wl_resource_instance_of(resource,
+ &zwlr_data_control_source_v1_interface, &source_impl));
+ return wl_resource_get_user_data(resource);
+}
+
+static void source_handle_offer(struct wl_client *client,
+ struct wl_resource *resource, const char *mime_type) {
+ struct client_data_source *source = source_from_resource(resource);
+ if (source == NULL) {
+ return;
+ }
+
+ if (source->finalized) {
+ wl_resource_post_error(resource,
+ ZWLR_DATA_CONTROL_SOURCE_V1_ERROR_INVALID_OFFER,
+ "cannot mutate offer after set_selection");
+ return;
+ }
+
+ char *dup_mime_type = strdup(mime_type);
+ if (dup_mime_type == NULL) {
+ wl_resource_post_no_memory(resource);
+ return;
+ }
+
+ char **p = wl_array_add(&source->source.mime_types, sizeof(char *));
+ if (p == NULL) {
+ free(dup_mime_type);
+ wl_resource_post_no_memory(resource);
+ return;
+ }
+
+ *p = dup_mime_type;
+}
+
+static void source_handle_destroy(struct wl_client *client,
+ struct wl_resource *resource) {
+ wl_resource_destroy(resource);
+}
+
+static const struct zwlr_data_control_source_v1_interface source_impl = {
+ .offer = source_handle_offer,
+ .destroy = source_handle_destroy,
+};
+
+static void source_handle_resource_destroy(struct wl_resource *resource) {
+ struct client_data_source *source = source_from_resource(resource);
+ if (source == NULL) {
+ return;
+ }
+ wlr_data_source_cancel(&source->source);
+}
+
+
+static const struct zwlr_data_control_offer_v1_interface offer_impl;
+
+static struct wlr_data_control_v1 *control_from_offer_resource(
+ struct wl_resource *resource) {
+ assert(wl_resource_instance_of(resource,
+ &zwlr_data_control_offer_v1_interface, &offer_impl));
+ return wl_resource_get_user_data(resource);
+}
+
+static void offer_handle_receive(struct wl_client *client,
+ struct wl_resource *resource, const char *mime_type, int fd) {
+ struct wlr_data_control_v1 *control = control_from_offer_resource(resource);
+ if (control == NULL || control->seat->selection_source == NULL) {
+ close(fd);
+ return;
+ }
+ wlr_data_source_send(control->seat->selection_source, mime_type, fd);
+}
+
+static void offer_handle_destroy(struct wl_client *client,
+ struct wl_resource *resource) {
+ wl_resource_destroy(resource);
+}
+
+static const struct zwlr_data_control_offer_v1_interface offer_impl = {
+ .receive = offer_handle_receive,
+ .destroy = offer_handle_destroy,
+};
+
+static void offer_handle_resource_destroy(struct wl_resource *resource) {
+ struct wlr_data_control_v1 *control = control_from_offer_resource(resource);
+ if (control != NULL) {
+ control->selection_offer_resource = NULL;
+ }
+}
+
+static struct wl_resource *create_offer(struct wlr_data_control_v1 *control,
+ struct wlr_data_source *source) {
+ struct wl_client *client = wl_resource_get_client(control->resource);
+ uint32_t version = wl_resource_get_version(control->resource);
+ struct wl_resource *resource = wl_resource_create(client,
+ &zwlr_data_control_offer_v1_interface, version, 0);
+ if (resource == NULL) {
+ return NULL;
+ }
+ wl_resource_set_implementation(resource, &offer_impl, control,
+ offer_handle_resource_destroy);
+
+ zwlr_data_control_v1_send_data_offer(control->resource, resource);
+
+ char **p;
+ wl_array_for_each(p, &source->mime_types) {
+ zwlr_data_control_offer_v1_send_offer(resource, *p);
+ }
+
+ return resource;
+}
+
+
+static const struct zwlr_data_control_v1_interface control_impl;
+
+static struct wlr_data_control_v1 *control_from_resource(
+ struct wl_resource *resource) {
+ assert(wl_resource_instance_of(resource,
+ &zwlr_data_control_v1_interface, &control_impl));
+ return wl_resource_get_user_data(resource);
+}
+
+static void control_handle_set_selection(struct wl_client *client,
+ struct wl_resource *control_resource,
+ struct wl_resource *source_resource) {
+ struct wlr_data_control_v1 *control =
+ control_from_resource(control_resource);
+ struct client_data_source *source = source_from_resource(source_resource);
+ if (control == NULL || source == NULL) {
+ return;
+ }
+
+ struct wl_display *display = wl_client_get_display(client);
+ wlr_seat_set_selection(control->seat, &source->source,
+ wl_display_next_serial(display));
+}
+
+static void control_handle_destroy(struct wl_client *client,
+ struct wl_resource *control_resource) {
+ wl_resource_destroy(control_resource);
+}
+
+static const struct zwlr_data_control_v1_interface control_impl = {
+ .set_selection = control_handle_set_selection,
+ .destroy = control_handle_destroy,
+};
+
+static void control_send_selection(struct wlr_data_control_v1 *control) {
+ struct wlr_data_source *source = control->seat->selection_source;
+
+ if (control->selection_offer_resource != NULL) {
+ // Make the offer inert
+ wl_resource_set_user_data(control->selection_offer_resource, NULL);
+ }
+
+ control->selection_offer_resource = NULL;
+ if (source != NULL) {
+ control->selection_offer_resource = create_offer(control, source);
+ if (control->selection_offer_resource == NULL) {
+ wl_resource_post_no_memory(control->resource);
+ return;
+ }
+ }
+
+ zwlr_data_control_v1_send_selection(control->resource,
+ control->selection_offer_resource);
+}
+
+static void control_handle_resource_destroy(struct wl_resource *resource) {
+ struct wlr_data_control_v1 *control = control_from_resource(resource);
+ wlr_data_control_v1_destroy(control);
+}
+
+static void control_handle_seat_destroy(struct wl_listener *listener,
+ void *data) {
+ struct wlr_data_control_v1 *control =
+ wl_container_of(listener, control, seat_destroy);
+ wlr_data_control_v1_destroy(control);
+}
+
+static void control_handle_seat_selection(struct wl_listener *listener,
+ void *data) {
+ struct wlr_data_control_v1 *control =
+ wl_container_of(listener, control, seat_selection);
+ control_send_selection(control);
+}
+
+void wlr_data_control_v1_destroy(struct wlr_data_control_v1 *control) {
+ if (control == NULL) {
+ return;
+ }
+ zwlr_data_control_v1_send_finished(control->resource);
+ // Make the resource inert
+ wl_resource_set_user_data(control->resource, NULL);
+ wl_list_remove(&control->seat_destroy.link);
+ wl_list_remove(&control->link);
+ free(control);
+}
+
+
+static const struct zwlr_data_control_manager_v1_interface manager_impl;
+
+static struct wlr_data_control_manager_v1 *manager_from_resource(
+ struct wl_resource *resource) {
+ assert(wl_resource_instance_of(resource,
+ &zwlr_data_control_manager_v1_interface, &manager_impl));
+ return wl_resource_get_user_data(resource);
+}
+
+static void manager_handle_create_data_source(struct wl_client *client,
+ struct wl_resource *manager_resource, uint32_t id) {
+ struct client_data_source *source =
+ calloc(1, sizeof(struct client_data_source));
+ if (source == NULL) {
+ wl_resource_post_no_memory(manager_resource);
+ return;
+ }
+ wlr_data_source_init(&source->source, &client_source_impl);
+
+ uint32_t version = wl_resource_get_version(manager_resource);
+ source->resource = wl_resource_create(client,
+ &zwlr_data_control_source_v1_interface, version, id);
+ if (source->resource == NULL) {
+ wl_resource_post_no_memory(manager_resource);
+ free(source);
+ return;
+ }
+ wl_resource_set_implementation(source->resource, &source_impl, source,
+ source_handle_resource_destroy);
+}
+
+static void manager_handle_get_data_control(struct wl_client *client,
+ struct wl_resource *manager_resource, uint32_t id,
+ struct wl_resource *seat_resource) {
+ struct wlr_data_control_manager_v1 *manager =
+ manager_from_resource(manager_resource);
+ struct wlr_seat_client *seat_client =
+ wlr_seat_client_from_resource(seat_resource);
+
+ struct wlr_data_control_v1 *control =
+ calloc(1, sizeof(struct wlr_data_control_v1));
+ if (control == NULL) {
+ wl_resource_post_no_memory(manager_resource);
+ return;
+ }
+ control->manager = manager;
+ control->seat = seat_client->seat;
+
+ uint32_t version = wl_resource_get_version(manager_resource);
+ control->resource = wl_resource_create(client,
+ &zwlr_data_control_v1_interface, version, id);
+ if (control->resource == NULL) {
+ wl_resource_post_no_memory(manager_resource);
+ free(control);
+ return;
+ }
+ wl_resource_set_implementation(control->resource, &control_impl, control,
+ control_handle_resource_destroy);
+ struct wl_resource *resource = control->resource;
+
+ control->seat_destroy.notify = control_handle_seat_destroy;
+ wl_signal_add(&control->seat->events.destroy, &control->seat_destroy);
+
+ control->seat_selection.notify = control_handle_seat_selection;
+ wl_signal_add(&control->seat->events.destroy, &control->seat_selection);
+
+ wl_list_insert(&manager->controls, &control->link);
+ wlr_signal_emit_safe(&manager->events.new_control, control);
+
+ // At this point maybe the compositor decided to destroy the control. If
+ // it's the case then the resource will be inert.
+ control = control_from_resource(resource);
+ if (control != NULL) {
+ control_send_selection(control);
+ }
+}
+
+static void manager_handle_destroy(struct wl_client *client,
+ struct wl_resource *manager_resource) {
+ wl_resource_destroy(manager_resource);
+}
+
+static const struct zwlr_data_control_manager_v1_interface manager_impl = {
+ .create_data_source = manager_handle_create_data_source,
+ .get_data_control = manager_handle_get_data_control,
+ .destroy = manager_handle_destroy,
+};
+
+static void manager_handle_resource_destroy(struct wl_resource *resource) {
+ wl_list_remove(wl_resource_get_link(resource));
+}
+
+static void manager_bind(struct wl_client *client, void *data, uint32_t version,
+ uint32_t id) {
+ struct wlr_data_control_manager_v1 *manager = data;
+
+ struct wl_resource *resource = wl_resource_create(client,
+ &zwlr_data_control_manager_v1_interface, version, id);
+ if (resource == NULL) {
+ wl_client_post_no_memory(client);
+ return;
+ }
+ wl_resource_set_implementation(resource, &manager_impl, manager,
+ manager_handle_resource_destroy);
+
+ wl_list_insert(&manager->resources, wl_resource_get_link(resource));
+}
+
+static void handle_display_destroy(struct wl_listener *listener, void *data) {
+ struct wlr_data_control_manager_v1 *manager =
+ wl_container_of(listener, manager, display_destroy);
+ wlr_data_control_manager_v1_destroy(manager);
+}
+
+struct wlr_data_control_manager_v1 *wlr_data_control_manager_v1_create(
+ struct wl_display *display) {
+ struct wlr_data_control_manager_v1 *manager =
+ calloc(1, sizeof(struct wlr_data_control_manager_v1));
+ if (manager == NULL) {
+ return NULL;
+ }
+ wl_list_init(&manager->resources);
+ wl_list_init(&manager->controls);
+ wl_signal_init(&manager->events.destroy);
+ wl_signal_init(&manager->events.new_control);
+
+ manager->global = wl_global_create(display,
+ &zwlr_data_control_manager_v1_interface, DATA_CONTROL_MANAGER_VERSION,
+ manager, manager_bind);
+ if (manager->global == NULL) {
+ free(manager);
+ return NULL;
+ }
+
+ manager->display_destroy.notify = handle_display_destroy;
+ wl_display_add_destroy_listener(display, &manager->display_destroy);
+
+ return manager;
+}
+
+void wlr_data_control_manager_v1_destroy(
+ struct wlr_data_control_manager_v1 *manager) {
+ if (manager == NULL) {
+ return;
+ }
+
+ wlr_signal_emit_safe(&manager->events.destroy, manager);
+
+ struct wlr_data_control_v1 *control, *control_tmp;
+ wl_list_for_each_safe(control, control_tmp, &manager->controls, link) {
+ wl_resource_destroy(control->resource);
+ }
+
+ struct wl_resource *resource, *resource_tmp;
+ wl_resource_for_each_safe(resource, resource_tmp, &manager->resources) {
+ wl_resource_destroy(resource);
+ }
+
+ wl_list_remove(&manager->display_destroy.link);
+ free(manager);
+}