From e9d2a6da5d86befbe6b3ed0dc2a5c7e958fcf806 Mon Sep 17 00:00:00 2001 From: Simon Zeni Date: Wed, 27 Aug 2025 14:51:20 -0400 Subject: [PATCH 1/2] types: remove support for wlr_data_control_v1 This is a legacy protocol superseded by ext-data-control-v1. Users should use `wlr_ext_data_control_manager_v1` as a replacement. --- include/wlr/types/wlr_data_control_v1.h | 57 -- protocol/meson.build | 1 - protocol/wlr-data-control-unstable-v1.xml | 278 --------- types/meson.build | 1 - types/wlr_data_control_v1.c | 701 ---------------------- 5 files changed, 1038 deletions(-) delete mode 100644 include/wlr/types/wlr_data_control_v1.h delete mode 100644 protocol/wlr-data-control-unstable-v1.xml delete mode 100644 types/wlr_data_control_v1.c diff --git a/include/wlr/types/wlr_data_control_v1.h b/include/wlr/types/wlr_data_control_v1.h deleted file mode 100644 index 4dd642bfe..000000000 --- a/include/wlr/types/wlr_data_control_v1.h +++ /dev/null @@ -1,57 +0,0 @@ -/* - * 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 -#include - -/** - * Deprecated: this protocol is legacy and superseded by ext-data-control-v1. - * The implementation will be dropped in a future wlroots version. - * - * Consider using `wlr_ext_data_control_manager_v1` as a replacement. - */ -struct wlr_data_control_manager_v1 { - struct wl_global *global; - struct wl_list devices; // wlr_data_control_device_v1.link - - struct { - struct wl_signal destroy; - struct wl_signal new_device; // wlr_data_control_device_v1 - } events; - - struct { - struct wl_listener display_destroy; - } WLR_PRIVATE; -}; - -struct wlr_data_control_device_v1 { - struct wl_resource *resource; - struct wlr_data_control_manager_v1 *manager; - struct wl_list link; // wlr_data_control_manager_v1.devices - - struct wlr_seat *seat; - struct wl_resource *selection_offer_resource; // current selection offer - struct wl_resource *primary_selection_offer_resource; // current primary selection offer - - struct { - struct wl_listener seat_destroy; - struct wl_listener seat_set_selection; - struct wl_listener seat_set_primary_selection; - } WLR_PRIVATE; -}; - -struct wlr_data_control_manager_v1 *wlr_data_control_manager_v1_create( - struct wl_display *display); - -void wlr_data_control_device_v1_destroy( - struct wlr_data_control_device_v1 *device); - -#endif diff --git a/protocol/meson.build b/protocol/meson.build index 5012753b5..c0fe3a890 100644 --- a/protocol/meson.build +++ b/protocol/meson.build @@ -66,7 +66,6 @@ protocols = { 'input-method-unstable-v2': 'input-method-unstable-v2.xml', 'kde-server-decoration': 'server-decoration.xml', 'virtual-keyboard-unstable-v1': 'virtual-keyboard-unstable-v1.xml', - 'wlr-data-control-unstable-v1': 'wlr-data-control-unstable-v1.xml', 'wlr-export-dmabuf-unstable-v1': 'wlr-export-dmabuf-unstable-v1.xml', 'wlr-foreign-toplevel-management-unstable-v1': 'wlr-foreign-toplevel-management-unstable-v1.xml', 'wlr-gamma-control-unstable-v1': 'wlr-gamma-control-unstable-v1.xml', diff --git a/protocol/wlr-data-control-unstable-v1.xml b/protocol/wlr-data-control-unstable-v1.xml deleted file mode 100644 index 75e8671b0..000000000 --- a/protocol/wlr-data-control-unstable-v1.xml +++ /dev/null @@ -1,278 +0,0 @@ - - - - Copyright © 2018 Simon Ser - Copyright © 2019 Ivan Molodetskikh - - 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. - - - - 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. - - - - - This interface is a manager that allows creating per-seat data device - controls. - - - - - Create a new data source. - - - - - - - Create a data device that can be used to manage a seat's selection. - - - - - - - - All objects created by the manager will still remain valid, until their - appropriate destroy request has been called. - - - - - - - This interface allows a client to manage a seat's selection. - - When the seat is destroyed, this object becomes inert. - - - - - This request asks the compositor to set the selection to the data from - the source on behalf of the client. - - The given source may not be used in any further set_selection or - set_primary_selection requests. Attempting to use a previously used - source is a protocol error. - - To unset the selection, set the source to NULL. - - - - - - - Destroys the data device object. - - - - - - The data_offer event introduces a new wlr_data_control_offer object, - which will subsequently be used in either the - wlr_data_control_device.selection event (for the regular clipboard - selections) or the wlr_data_control_device.primary_selection event (for - the primary clipboard selections). Immediately following the - wlr_data_control_device.data_offer event, the new data_offer object - will send out wlr_data_control_offer.offer events to describe the MIME - types it offers. - - - - - - - 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_device.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. - - The first selection event is sent upon binding the - wlr_data_control_device object. - - - - - - - This data control object is no longer valid and should be destroyed by - the client. - - - - - - - - The primary_selection event is sent out to notify the client of a new - wlr_data_control_offer for the primary selection for this device. The - wlr_data_control_device.data_offer and the wlr_data_control_offer.offer - events are sent out immediately before this event to introduce the data - offer object. The primary_selection event is sent to a client when a - new primary 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 primary selection wlr_data_control_offer, if any, - upon receiving this event. - - If the compositor supports primary selection, the first - primary_selection event is sent upon binding the - wlr_data_control_device object. - - - - - - - This request asks the compositor to set the primary selection to the - data from the source on behalf of the client. - - The given source may not be used in any further set_selection or - set_primary_selection requests. Attempting to use a previously used - source is a protocol error. - - To unset the primary selection, set the source to NULL. - - The compositor will ignore this request if it does not support primary - selection. - - - - - - - - - - - - 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. - - - - - - - - - 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_device.set_selection is a protocol - error. - - - - - - - Destroys the data source object. - - - - - - Request for data from the client. Send the data as the specified MIME - type over the passed file descriptor, then close it. - - - - - - - - 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. - - - - - - - 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. - - - - - 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. - - - - - - - - Destroys the data offer object. - - - - - - Sent immediately after creating the wlr_data_control_offer object. - One event per offered MIME type. - - - - - diff --git a/types/meson.build b/types/meson.build index 402fd3e11..941d7c33e 100644 --- a/types/meson.build +++ b/types/meson.build @@ -45,7 +45,6 @@ wlr_files += files( 'wlr_cursor.c', 'wlr_cursor_shape_v1.c', 'wlr_damage_ring.c', - 'wlr_data_control_v1.c', 'wlr_drm.c', 'wlr_export_dmabuf_v1.c', 'wlr_ext_data_control_v1.c', diff --git a/types/wlr_data_control_v1.c b/types/wlr_data_control_v1.c deleted file mode 100644 index 2e26d703d..000000000 --- a/types/wlr_data_control_v1.c +++ /dev/null @@ -1,701 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include "wlr-data-control-unstable-v1-protocol.h" - -#define DATA_CONTROL_MANAGER_VERSION 2 - -struct data_control_source { - struct wl_resource *resource; - struct wl_array mime_types; - bool finalized; - - // Only one of these is non-NULL. - struct wlr_data_source *active_source; - struct wlr_primary_selection_source *active_primary_source; -}; - -static const struct zwlr_data_control_source_v1_interface source_impl; - -static struct data_control_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 data_control_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 or " - "set_primary_selection"); - return; - } - - const char **mime_type_ptr; - wl_array_for_each(mime_type_ptr, &source->mime_types) { - if (strcmp(*mime_type_ptr, mime_type) == 0) { - wlr_log(WLR_DEBUG, "Ignoring duplicate MIME type offer %s", - mime_type); - 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->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 data_control_source_destroy(struct data_control_source *source) { - if (source == NULL) { - return; - } - - char **p; - wl_array_for_each(p, &source->mime_types) { - free(*p); - } - wl_array_release(&source->mime_types); - - // Prevent destructors below from calling this recursively. - wl_resource_set_user_data(source->resource, NULL); - - if (source->active_source != NULL) { - wlr_data_source_destroy(source->active_source); - } else if (source->active_primary_source != NULL) { - wlr_primary_selection_source_destroy( - source->active_primary_source); - } - - free(source); -} - -static void source_handle_resource_destroy(struct wl_resource *resource) { - struct data_control_source *source = source_from_resource(resource); - data_control_source_destroy(source); -} - - -struct client_data_source { - struct wlr_data_source source; - struct wl_resource *resource; -}; - -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); - struct client_data_source *source = wl_container_of(wlr_source, source, source); - return 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_destroy(struct wlr_data_source *wlr_source) { - struct client_data_source *client_source = - client_data_source_from_source(wlr_source); - struct data_control_source *source = - source_from_resource(client_source->resource); - free(client_source); - - if (source == NULL) { - return; - } - - source->active_source = NULL; - - zwlr_data_control_source_v1_send_cancelled(source->resource); - data_control_source_destroy(source); -} - -static const struct wlr_data_source_impl client_source_impl = { - .send = client_source_send, - .destroy = client_source_destroy, -}; - - -struct client_primary_selection_source { - struct wlr_primary_selection_source source; - struct wl_resource *resource; -}; - -static const struct wlr_primary_selection_source_impl -client_primary_selection_source_impl; - -static struct client_primary_selection_source * - client_primary_selection_source_from_source( - struct wlr_primary_selection_source *wlr_source) { - assert(wlr_source->impl == &client_primary_selection_source_impl); - struct client_primary_selection_source *source = wl_container_of(wlr_source, source, source); - return source; -} - -static void client_primary_selection_source_send( - struct wlr_primary_selection_source *wlr_source, - const char *mime_type, int fd) { - struct client_primary_selection_source *source = - client_primary_selection_source_from_source(wlr_source); - zwlr_data_control_source_v1_send_send(source->resource, mime_type, fd); - close(fd); -} - -static void client_primary_selection_source_destroy( - struct wlr_primary_selection_source *wlr_source) { - struct client_primary_selection_source *client_source = - client_primary_selection_source_from_source(wlr_source); - struct data_control_source *source = - source_from_resource(client_source->resource); - free(client_source); - - if (source == NULL) { - return; - } - - source->active_primary_source = NULL; - - zwlr_data_control_source_v1_send_cancelled(source->resource); - data_control_source_destroy(source); -} - -static const struct wlr_primary_selection_source_impl -client_primary_selection_source_impl = { - .send = client_primary_selection_source_send, - .destroy = client_primary_selection_source_destroy, -}; - - -struct data_offer { - struct wl_resource *resource; - struct wlr_data_control_device_v1 *device; - bool is_primary; -}; - -static void data_offer_destroy(struct data_offer *offer) { - if (offer == NULL) { - return; - } - - struct wlr_data_control_device_v1 *device = offer->device; - if (device != NULL) { - if (offer->is_primary) { - device->primary_selection_offer_resource = NULL; - } else { - device->selection_offer_resource = NULL; - } - } - - wl_resource_set_user_data(offer->resource, NULL); - free(offer); -} - -static const struct zwlr_data_control_offer_v1_interface offer_impl; - -static struct data_offer *data_offer_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 data_offer *offer = data_offer_from_offer_resource(resource); - if (offer == NULL) { - close(fd); - return; - } - - struct wlr_data_control_device_v1 *device = offer->device; - if (device == NULL) { - close(fd); - return; - } - - if (offer->is_primary) { - if (device->seat->primary_selection_source == NULL) { - close(fd); - return; - } - wlr_primary_selection_source_send( - device->seat->primary_selection_source, - mime_type, fd); - } else { - if (device->seat->selection_source == NULL) { - close(fd); - return; - } - wlr_data_source_send(device->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 data_offer *offer = data_offer_from_offer_resource(resource); - data_offer_destroy(offer); -} - -static struct wl_resource *create_offer(struct wlr_data_control_device_v1 *device, - struct wl_array *mime_types, bool is_primary) { - struct wl_client *client = wl_resource_get_client(device->resource); - - struct data_offer *offer = calloc(1, sizeof(*offer)); - if (offer == NULL) { - wl_client_post_no_memory(client); - return NULL; - } - - offer->device = device; - offer->is_primary = is_primary; - - uint32_t version = wl_resource_get_version(device->resource); - struct wl_resource *resource = wl_resource_create(client, - &zwlr_data_control_offer_v1_interface, version, 0); - if (resource == NULL) { - free(offer); - return NULL; - } - - offer->resource = resource; - - wl_resource_set_implementation(resource, &offer_impl, offer, - offer_handle_resource_destroy); - - zwlr_data_control_device_v1_send_data_offer(device->resource, resource); - - char **p; - wl_array_for_each(p, mime_types) { - zwlr_data_control_offer_v1_send_offer(resource, *p); - } - - return resource; -} - - -static const struct zwlr_data_control_device_v1_interface control_impl; - -static struct wlr_data_control_device_v1 *control_from_resource( - struct wl_resource *resource) { - assert(wl_resource_instance_of(resource, - &zwlr_data_control_device_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_device_v1 *device = - control_from_resource(control_resource); - if (device == NULL) { - return; - } - - struct data_control_source *source = NULL; - if (source_resource != NULL) { - source = source_from_resource(source_resource); - } - - if (source == NULL) { - wlr_seat_request_set_selection(device->seat, NULL, NULL, - wl_display_next_serial(device->seat->display)); - - return; - } - - if (source->active_source != NULL || - source->active_primary_source != NULL) { - wl_resource_post_error(control_resource, - ZWLR_DATA_CONTROL_DEVICE_V1_ERROR_USED_SOURCE, - "cannot use a data source in set_selection or " - "set_primary_selection more than once"); - - return; - } - - struct client_data_source *client_source = calloc(1, sizeof(*client_source)); - if (client_source == NULL) { - wl_client_post_no_memory(client); - return; - } - client_source->resource = source_resource; - - struct wlr_data_source *wlr_source = &client_source->source; - wlr_data_source_init(wlr_source, &client_source_impl); - source->active_source = wlr_source; - - wl_array_release(&wlr_source->mime_types); - wlr_source->mime_types = source->mime_types; - wl_array_init(&source->mime_types); - - source->finalized = true; - - wlr_seat_request_set_selection(device->seat, NULL, wlr_source, - wl_display_next_serial(device->seat->display)); -} - -static void control_handle_set_primary_selection(struct wl_client *client, - struct wl_resource *control_resource, - struct wl_resource *source_resource) { - struct wlr_data_control_device_v1 *device = - control_from_resource(control_resource); - if (device == NULL) { - return; - } - - struct data_control_source *source = NULL; - if (source_resource != NULL) { - source = source_from_resource(source_resource); - } - - if (source == NULL) { - wlr_seat_request_set_primary_selection(device->seat, NULL, NULL, - wl_display_next_serial(device->seat->display)); - - return; - } - - if (source->active_source != NULL || - source->active_primary_source != NULL) { - wl_resource_post_error(control_resource, - ZWLR_DATA_CONTROL_DEVICE_V1_ERROR_USED_SOURCE, - "cannot use a data source in set_selection or " - "set_primary_selection more than once"); - - return; - } - - struct client_primary_selection_source *client_source = calloc(1, sizeof(*client_source)); - if (client_source == NULL) { - wl_client_post_no_memory(client); - return; - } - client_source->resource = source_resource; - - struct wlr_primary_selection_source *wlr_source = &client_source->source; - wlr_primary_selection_source_init(wlr_source, &client_primary_selection_source_impl); - source->active_primary_source = wlr_source; - - wl_array_release(&wlr_source->mime_types); - wlr_source->mime_types = source->mime_types; - wl_array_init(&source->mime_types); - - source->finalized = true; - - wlr_seat_request_set_primary_selection(device->seat, NULL, wlr_source, - wl_display_next_serial(device->seat->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_device_v1_interface control_impl = { - .set_selection = control_handle_set_selection, - .set_primary_selection = control_handle_set_primary_selection, - .destroy = control_handle_destroy, -}; - -static void control_send_selection(struct wlr_data_control_device_v1 *device) { - struct wlr_data_source *source = device->seat->selection_source; - - if (device->selection_offer_resource != NULL) { - // Make the offer inert - struct data_offer *offer = data_offer_from_offer_resource( - device->selection_offer_resource); - data_offer_destroy(offer); - } - - device->selection_offer_resource = NULL; - if (source != NULL) { - device->selection_offer_resource = - create_offer(device, &source->mime_types, false); - if (device->selection_offer_resource == NULL) { - wl_resource_post_no_memory(device->resource); - return; - } - } - - zwlr_data_control_device_v1_send_selection(device->resource, - device->selection_offer_resource); -} - -static void control_send_primary_selection( - struct wlr_data_control_device_v1 *device) { - uint32_t version = wl_resource_get_version(device->resource); - if (version < ZWLR_DATA_CONTROL_DEVICE_V1_PRIMARY_SELECTION_SINCE_VERSION) { - return; - } - - struct wlr_primary_selection_source *source = - device->seat->primary_selection_source; - - if (device->primary_selection_offer_resource != NULL) { - // Make the offer inert - struct data_offer *offer = data_offer_from_offer_resource( - device->primary_selection_offer_resource); - data_offer_destroy(offer); - } - - device->primary_selection_offer_resource = NULL; - if (source != NULL) { - device->primary_selection_offer_resource = - create_offer(device, &source->mime_types, true); - if (device->primary_selection_offer_resource == NULL) { - wl_resource_post_no_memory(device->resource); - return; - } - } - - zwlr_data_control_device_v1_send_primary_selection(device->resource, - device->primary_selection_offer_resource); -} - -static void control_handle_resource_destroy(struct wl_resource *resource) { - struct wlr_data_control_device_v1 *device = control_from_resource(resource); - wlr_data_control_device_v1_destroy(device); -} - -static void control_handle_seat_destroy(struct wl_listener *listener, - void *data) { - struct wlr_data_control_device_v1 *device = - wl_container_of(listener, device, seat_destroy); - wlr_data_control_device_v1_destroy(device); -} - -static void control_handle_seat_set_selection(struct wl_listener *listener, - void *data) { - struct wlr_data_control_device_v1 *device = - wl_container_of(listener, device, seat_set_selection); - control_send_selection(device); -} - -static void control_handle_seat_set_primary_selection( - struct wl_listener *listener, - void *data) { - struct wlr_data_control_device_v1 *device = - wl_container_of(listener, device, seat_set_primary_selection); - control_send_primary_selection(device); -} - -void wlr_data_control_device_v1_destroy(struct wlr_data_control_device_v1 *device) { - if (device == NULL) { - return; - } - zwlr_data_control_device_v1_send_finished(device->resource); - // Make the resources inert - wl_resource_set_user_data(device->resource, NULL); - if (device->selection_offer_resource != NULL) { - struct data_offer *offer = data_offer_from_offer_resource( - device->selection_offer_resource); - data_offer_destroy(offer); - } - if (device->primary_selection_offer_resource != NULL) { - struct data_offer *offer = data_offer_from_offer_resource( - device->primary_selection_offer_resource); - data_offer_destroy(offer); - } - wl_list_remove(&device->seat_destroy.link); - wl_list_remove(&device->seat_set_selection.link); - wl_list_remove(&device->seat_set_primary_selection.link); - wl_list_remove(&device->link); - free(device); -} - - -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 data_control_source *source = calloc(1, sizeof(*source)); - if (source == NULL) { - wl_resource_post_no_memory(manager_resource); - return; - } - - wl_array_init(&source->mime_types); - - 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); - wl_array_release(&source->mime_types); - free(source); - return; - } - wl_resource_set_implementation(source->resource, &source_impl, source, - source_handle_resource_destroy); -} - -static void manager_handle_get_data_device(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); - - uint32_t version = wl_resource_get_version(manager_resource); - struct wl_resource *resource = wl_resource_create(client, - &zwlr_data_control_device_v1_interface, version, id); - if (resource == NULL) { - wl_resource_post_no_memory(manager_resource); - return; - } - wl_resource_set_implementation(resource, &control_impl, NULL, - control_handle_resource_destroy); - if (seat_client == NULL) { - return; - } - - struct wlr_data_control_device_v1 *device = calloc(1, sizeof(*device)); - if (device == NULL) { - wl_resource_post_no_memory(manager_resource); - return; - } - device->manager = manager; - device->seat = seat_client->seat; - device->resource = resource; - wl_resource_set_user_data(resource, device); - - device->seat_destroy.notify = control_handle_seat_destroy; - wl_signal_add(&device->seat->events.destroy, &device->seat_destroy); - - device->seat_set_selection.notify = control_handle_seat_set_selection; - wl_signal_add(&device->seat->events.set_selection, - &device->seat_set_selection); - - device->seat_set_primary_selection.notify = - control_handle_seat_set_primary_selection; - wl_signal_add(&device->seat->events.set_primary_selection, - &device->seat_set_primary_selection); - - wl_list_insert(&manager->devices, &device->link); - wl_signal_emit_mutable(&manager->events.new_device, device); - - // At this point maybe the compositor decided to destroy the device. If - // it's the case then the resource will be inert. - device = control_from_resource(resource); - if (device != NULL) { - control_send_selection(device); - control_send_primary_selection(device); - } -} - -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_device = manager_handle_get_data_device, - .destroy = manager_handle_destroy, -}; - -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, NULL); -} - -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); - wl_signal_emit_mutable(&manager->events.destroy, manager); - - assert(wl_list_empty(&manager->events.destroy.listener_list)); - assert(wl_list_empty(&manager->events.new_device.listener_list)); - - wl_list_remove(&manager->display_destroy.link); - wl_global_destroy(manager->global); - free(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(*manager)); - if (manager == NULL) { - return NULL; - } - wl_list_init(&manager->devices); - - wl_signal_init(&manager->events.destroy); - wl_signal_init(&manager->events.new_device); - - 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; -} From 879243e370de6167d2c49510396f937b1a93fab5 Mon Sep 17 00:00:00 2001 From: David Turner Date: Mon, 20 Oct 2025 13:55:00 +0100 Subject: [PATCH 2/2] xwm: Fix double-close When an FD is passed to xcb_connect_to_fd(), xcb takes ownership of that FD and is responsible for closing it, which it does when xcb_disconnect() is called. But the xwayland handler code also keeps a copy of the FD and closes it via safe_close() in server_finish_process(). This double-close can cause all sorts of problems if another part of wlroots allocates another FD between the two closes - the latter close will close the wrong FD and things go horribly wrong (in my case leading to use-after-free and segfaults). Fix this by setting wm_fd[0]=-1 after calling xwm_create(), and ensuring that xwm_create() closes the FD if startup errors occur. --- include/xwayland/xwm.h | 1 + xwayland/xwayland.c | 3 +++ xwayland/xwm.c | 3 +++ 3 files changed, 7 insertions(+) diff --git a/include/xwayland/xwm.h b/include/xwayland/xwm.h index e460bbb63..73f440d29 100644 --- a/include/xwayland/xwm.h +++ b/include/xwayland/xwm.h @@ -164,6 +164,7 @@ struct wlr_xwm { struct wl_listener drop_focus_destroy; }; +// xwm_create takes ownership of wm_fd and will close it under all circumstances. struct wlr_xwm *xwm_create(struct wlr_xwayland *wlr_xwayland, int wm_fd); void xwm_destroy(struct wlr_xwm *xwm); diff --git a/xwayland/xwayland.c b/xwayland/xwayland.c index 5d51df074..3aa47bac2 100644 --- a/xwayland/xwayland.c +++ b/xwayland/xwayland.c @@ -42,6 +42,9 @@ static void handle_server_start(struct wl_listener *listener, void *data) { static void xwayland_mark_ready(struct wlr_xwayland *xwayland) { assert(xwayland->server->wm_fd[0] >= 0); xwayland->xwm = xwm_create(xwayland, xwayland->server->wm_fd[0]); + // xwm_create takes ownership of wm_fd[0] under all circumstances + xwayland->server->wm_fd[0] = -1; + if (!xwayland->xwm) { return; } diff --git a/xwayland/xwm.c b/xwayland/xwm.c index a82e8b145..2bb4e4c64 100644 --- a/xwayland/xwm.c +++ b/xwayland/xwm.c @@ -2530,6 +2530,7 @@ void xwm_set_cursor(struct wlr_xwm *xwm, const uint8_t *pixels, uint32_t stride, struct wlr_xwm *xwm_create(struct wlr_xwayland *xwayland, int wm_fd) { struct wlr_xwm *xwm = calloc(1, sizeof(*xwm)); if (xwm == NULL) { + close(wm_fd); return NULL; } @@ -2544,11 +2545,13 @@ struct wlr_xwm *xwm_create(struct wlr_xwayland *xwayland, int wm_fd) { xwm->ping_timeout = 10000; + // xcb_connect_to_fd takes ownership of the FD regardless of success/failure xwm->xcb_conn = xcb_connect_to_fd(wm_fd, NULL); int rc = xcb_connection_has_error(xwm->xcb_conn); if (rc) { wlr_log(WLR_ERROR, "xcb connect failed: %d", rc); + xcb_disconnect(xwm->xcb_conn); free(xwm); return NULL; }