The standard primary-selection protocol is now widely supported.master
							parent
							
								
									a109a80dca
								
							
						
					
					
						commit
						78befa59f9
					
				| @ -1,57 +0,0 @@ | |||||||
| /*
 |  | ||||||
|  * This protocol is obsolete and will be removed in a future version. The |  | ||||||
|  * recommended replacement is wp-primary-selection. |  | ||||||
|  */ |  | ||||||
| 
 |  | ||||||
| /*
 |  | ||||||
|  * 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_GTK_PRIMARY_SELECTION_H |  | ||||||
| #define WLR_TYPES_WLR_GTK_PRIMARY_SELECTION_H |  | ||||||
| 
 |  | ||||||
| #include <wayland-server-core.h> |  | ||||||
| #include <wlr/types/wlr_seat.h> |  | ||||||
| 
 |  | ||||||
| /**
 |  | ||||||
|  * A primary selection manager which implements the GTK protocol. |  | ||||||
|  */ |  | ||||||
| struct wlr_gtk_primary_selection_device_manager { |  | ||||||
| 	struct wl_global *global; |  | ||||||
| 	struct wl_list devices; // wlr_gtk_primary_selection_device::link
 |  | ||||||
| 
 |  | ||||||
| 	struct wl_listener display_destroy; |  | ||||||
| 
 |  | ||||||
| 	struct { |  | ||||||
| 		struct wl_signal destroy; |  | ||||||
| 	} events; |  | ||||||
| 
 |  | ||||||
| 	void *data; |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| /**
 |  | ||||||
|  * A device is a per-seat object used to set and get the current selection. |  | ||||||
|  */ |  | ||||||
| struct wlr_gtk_primary_selection_device { |  | ||||||
| 	struct wlr_gtk_primary_selection_device_manager *manager; |  | ||||||
| 	struct wlr_seat *seat; |  | ||||||
| 	struct wl_list link; // wlr_gtk_primary_selection_device_manager::devices
 |  | ||||||
| 	struct wl_list resources; // wl_resource_get_link
 |  | ||||||
| 
 |  | ||||||
| 	struct wl_list offers; // wl_resource_get_link
 |  | ||||||
| 
 |  | ||||||
| 	struct wl_listener seat_destroy; |  | ||||||
| 	struct wl_listener seat_focus_change; |  | ||||||
| 	struct wl_listener seat_set_primary_selection; |  | ||||||
| 
 |  | ||||||
| 	void *data; |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| struct wlr_gtk_primary_selection_device_manager * |  | ||||||
| 	wlr_gtk_primary_selection_device_manager_create(struct wl_display *display); |  | ||||||
| 
 |  | ||||||
| #endif |  | ||||||
| @ -1,225 +0,0 @@ | |||||||
| <?xml version="1.0" encoding="UTF-8"?> |  | ||||||
| <protocol name="gtk_primary_selection"> |  | ||||||
|   <copyright> |  | ||||||
|     Copyright © 2015, 2016 Red Hat |  | ||||||
| 
 |  | ||||||
|     Permission is hereby granted, free of charge, to any person obtaining a |  | ||||||
|     copy of this software and associated documentation files (the "Software"), |  | ||||||
|     to deal in the Software without restriction, including without limitation |  | ||||||
|     the rights to use, copy, modify, merge, publish, distribute, sublicense, |  | ||||||
|     and/or sell copies of the Software, and to permit persons to whom the |  | ||||||
|     Software is furnished to do so, subject to the following conditions: |  | ||||||
| 
 |  | ||||||
|     The above copyright notice and this permission notice (including the next |  | ||||||
|     paragraph) shall be included in all copies or substantial portions of the |  | ||||||
|     Software. |  | ||||||
| 
 |  | ||||||
|     THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |  | ||||||
|     IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |  | ||||||
|     FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL |  | ||||||
|     THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |  | ||||||
|     LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |  | ||||||
|     FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER |  | ||||||
|     DEALINGS IN THE SOFTWARE. |  | ||||||
|   </copyright> |  | ||||||
| 
 |  | ||||||
|   <description summary="Primary selection protocol"> |  | ||||||
|     This protocol provides the ability to have a primary selection device to |  | ||||||
|     match that of the X server. This primary selection is a shortcut to the |  | ||||||
|     common clipboard selection, where text just needs to be selected in order |  | ||||||
|     to allow copying it elsewhere. The de facto way to perform this action |  | ||||||
|     is the middle mouse button, although it is not limited to this one. |  | ||||||
| 
 |  | ||||||
|     Clients wishing to honor primary selection should create a primary |  | ||||||
|     selection source and set it as the selection through |  | ||||||
|     wp_primary_selection_device.set_selection whenever the text selection |  | ||||||
|     changes. In order to minimize calls in pointer-driven text selection, |  | ||||||
|     it should happen only once after the operation finished. Similarly, |  | ||||||
|     a NULL source should be set when text is unselected. |  | ||||||
| 
 |  | ||||||
|     wp_primary_selection_offer objects are first announced through the |  | ||||||
|     wp_primary_selection_device.data_offer event. Immediately after this event, |  | ||||||
|     the primary data offer will emit wp_primary_selection_offer.offer events |  | ||||||
|     to let know of the mime types being offered. |  | ||||||
| 
 |  | ||||||
|     When the primary selection changes, the client with the keyboard focus |  | ||||||
|     will receive wp_primary_selection_device.selection events. Only the client |  | ||||||
|     with the keyboard focus will receive such events with a non-NULL |  | ||||||
|     wp_primary_selection_offer. Across keyboard focus changes, previously |  | ||||||
|     focused clients will receive wp_primary_selection_device.events with a |  | ||||||
|     NULL wp_primary_selection_offer. |  | ||||||
| 
 |  | ||||||
|     In order to request the primary selection data, the client must pass |  | ||||||
|     a recent serial pertaining to the press event that is triggering the |  | ||||||
|     operation, if the compositor deems the serial valid and recent, the |  | ||||||
|     wp_primary_selection_source.send event will happen in the other end |  | ||||||
|     to let the transfer begin. The client owning the primary selection |  | ||||||
|     should write the requested data, and close the file descriptor |  | ||||||
|     immediately. |  | ||||||
| 
 |  | ||||||
|     If the primary selection owner client disappeared during the transfer, |  | ||||||
|     the client reading the data will receive a |  | ||||||
|     wp_primary_selection_device.selection event with a NULL |  | ||||||
|     wp_primary_selection_offer, the client should take this as a hint |  | ||||||
|     to finish the reads related to the no longer existing offer. |  | ||||||
| 
 |  | ||||||
|     The primary selection owner should be checking for errors during |  | ||||||
|     writes, merely cancelling the ongoing transfer if any happened. |  | ||||||
|   </description> |  | ||||||
| 
 |  | ||||||
|   <interface name="gtk_primary_selection_device_manager" version="1"> |  | ||||||
|     <description summary="X primary selection emulation"> |  | ||||||
|       The primary selection device manager is a singleton global object that |  | ||||||
|       provides access to the primary selection. It allows to create |  | ||||||
|       wp_primary_selection_source objects, as well as retrieving the per-seat |  | ||||||
|       wp_primary_selection_device objects. |  | ||||||
|     </description> |  | ||||||
| 
 |  | ||||||
|     <request name="create_source"> |  | ||||||
|       <description summary="create a new primary selection source"> |  | ||||||
| 	Create a new primary selection source. |  | ||||||
|       </description> |  | ||||||
|       <arg name="id" type="new_id" interface="gtk_primary_selection_source"/> |  | ||||||
|     </request> |  | ||||||
| 
 |  | ||||||
|     <request name="get_device"> |  | ||||||
|       <description summary="create a new primary selection device"> |  | ||||||
|         Create a new data device for a given seat. |  | ||||||
|       </description> |  | ||||||
|       <arg name="id" type="new_id" interface="gtk_primary_selection_device"/> |  | ||||||
|       <arg name="seat" type="object" interface="wl_seat"/> |  | ||||||
|     </request> |  | ||||||
| 
 |  | ||||||
|     <request name="destroy" type="destructor"> |  | ||||||
|       <description summary="destroy the primary selection device manager"> |  | ||||||
| 	Destroy the primary selection device manager. |  | ||||||
|       </description> |  | ||||||
|     </request> |  | ||||||
|   </interface> |  | ||||||
| 
 |  | ||||||
|   <interface name="gtk_primary_selection_device" version="1"> |  | ||||||
|     <request name="set_selection"> |  | ||||||
|       <description summary="set the primary selection"> |  | ||||||
| 	Replaces the current selection. The previous owner of the primary selection |  | ||||||
| 	will receive a wp_primary_selection_source.cancelled event. |  | ||||||
| 
 |  | ||||||
| 	To unset the selection, set the source to NULL. |  | ||||||
|       </description> |  | ||||||
|       <arg name="source" type="object" interface="gtk_primary_selection_source" allow-null="true"/> |  | ||||||
|       <arg name="serial" type="uint" summary="serial of the event that triggered this request"/> |  | ||||||
|     </request> |  | ||||||
| 
 |  | ||||||
|     <event name="data_offer"> |  | ||||||
|       <description summary="introduce a new wp_primary_selection_offer"> |  | ||||||
| 	Introduces a new wp_primary_selection_offer object that may be used |  | ||||||
| 	to receive the current primary selection. Immediately following this |  | ||||||
| 	event, the new wp_primary_selection_offer object will send |  | ||||||
| 	wp_primary_selection_offer.offer events to describe the offered mime |  | ||||||
| 	types. |  | ||||||
|       </description> |  | ||||||
|       <arg name="offer" type="new_id" interface="gtk_primary_selection_offer"/> |  | ||||||
|     </event> |  | ||||||
| 
 |  | ||||||
|     <event name="selection"> |  | ||||||
|       <description summary="advertise a new primary selection"> |  | ||||||
| 	The wp_primary_selection_device.selection event is sent to notify the |  | ||||||
| 	client of a new primary selection. This event is sent after the |  | ||||||
| 	wp_primary_selection.data_offer event introducing this object, and after |  | ||||||
| 	the offer has announced its mimetypes through |  | ||||||
| 	wp_primary_selection_offer.offer. |  | ||||||
| 
 |  | ||||||
| 	The data_offer is valid until a new offer or NULL is received |  | ||||||
| 	or until the client loses keyboard focus. The client must destroy the |  | ||||||
| 	previous selection data_offer, if any, upon receiving this event. |  | ||||||
|       </description> |  | ||||||
|       <arg name="id" type="object" interface="gtk_primary_selection_offer" allow-null="true"/> |  | ||||||
|     </event> |  | ||||||
| 
 |  | ||||||
|     <request name="destroy" type="destructor"> |  | ||||||
|       <description summary="destroy the primary selection device"> |  | ||||||
| 	Destroy the primary selection device. |  | ||||||
|       </description> |  | ||||||
|     </request> |  | ||||||
|   </interface> |  | ||||||
| 
 |  | ||||||
|   <interface name="gtk_primary_selection_offer" version="1"> |  | ||||||
|     <description summary="offer to transfer primary selection contents"> |  | ||||||
|       A wp_primary_selection_offer represents an offer to transfer the contents |  | ||||||
|       of the primary selection clipboard to the client. Similar to |  | ||||||
|       wl_data_offer, the offer also describes the mime types that the source |  | ||||||
|       will transferthat the |  | ||||||
|       data can be converted to and provides the mechanisms for transferring the |  | ||||||
|       data directly to the client. |  | ||||||
|     </description> |  | ||||||
| 
 |  | ||||||
|     <request name="receive"> |  | ||||||
|       <description summary="request that the data is transferred"> |  | ||||||
| 	To transfer the contents of the primary selection clipboard, the client |  | ||||||
| 	issues this request and indicates the mime type that 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 |  | ||||||
| 	closes its end, at which point the transfer is complete. |  | ||||||
|       </description> |  | ||||||
|       <arg name="mime_type" type="string"/> |  | ||||||
|       <arg name="fd" type="fd"/> |  | ||||||
|     </request> |  | ||||||
| 
 |  | ||||||
|     <request name="destroy" type="destructor"> |  | ||||||
|       <description summary="destroy the primary selection offer"> |  | ||||||
| 	Destroy the primary selection offer. |  | ||||||
|       </description> |  | ||||||
|     </request> |  | ||||||
| 
 |  | ||||||
|     <event name="offer"> |  | ||||||
|       <description summary="advertise offered mime type"> |  | ||||||
| 	Sent immediately after creating announcing the wp_primary_selection_offer |  | ||||||
| 	through wp_primary_selection_device.data_offer. One event is sent per |  | ||||||
| 	offered mime type. |  | ||||||
|       </description> |  | ||||||
|       <arg name="mime_type" type="string"/> |  | ||||||
|     </event> |  | ||||||
|   </interface> |  | ||||||
| 
 |  | ||||||
|   <interface name="gtk_primary_selection_source" version="1"> |  | ||||||
|     <description summary="offer to replace the contents of the primary selection"> |  | ||||||
|       The source side of a wp_primary_selection_offer, it provides a way to |  | ||||||
|       describe the offered data and respond to requests to transfer the |  | ||||||
|       requested contents of the primary selection clipboard. |  | ||||||
|     </description> |  | ||||||
| 
 |  | ||||||
|     <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. |  | ||||||
|       </description> |  | ||||||
|       <arg name="mime_type" type="string"/> |  | ||||||
|     </request> |  | ||||||
| 
 |  | ||||||
|     <request name="destroy" type="destructor"> |  | ||||||
|       <description summary="destroy the primary selection source"> |  | ||||||
| 	Destroy the primary selection source. |  | ||||||
|       </description> |  | ||||||
|     </request> |  | ||||||
| 
 |  | ||||||
|     <event name="send"> |  | ||||||
|       <description summary="send the primary selection contents"> |  | ||||||
| 	Request for the current primary selection contents from the client. |  | ||||||
| 	Send the specified mime type over the passed file descriptor, then |  | ||||||
| 	close it. |  | ||||||
|       </description> |  | ||||||
|       <arg name="mime_type" type="string"/> |  | ||||||
|       <arg name="fd" type="fd"/> |  | ||||||
|     </event> |  | ||||||
| 
 |  | ||||||
|     <event name="cancelled"> |  | ||||||
|       <description summary="request for primary selection contents was canceled"> |  | ||||||
| 	This primary selection source is no longer valid. The client should |  | ||||||
| 	clean up and destroy this primary selection source. |  | ||||||
|       </description> |  | ||||||
|     </event> |  | ||||||
|   </interface> |  | ||||||
| </protocol> |  | ||||||
| @ -1,490 +0,0 @@ | |||||||
| #define _POSIX_C_SOURCE 200809L |  | ||||||
| #include <assert.h> |  | ||||||
| #include <inttypes.h> |  | ||||||
| #include <stdlib.h> |  | ||||||
| #include <string.h> |  | ||||||
| #include <unistd.h> |  | ||||||
| #include <wlr/types/wlr_gtk_primary_selection.h> |  | ||||||
| #include <wlr/types/wlr_primary_selection.h> |  | ||||||
| #include <wlr/types/wlr_seat.h> |  | ||||||
| #include <wlr/util/log.h> |  | ||||||
| #include "gtk-primary-selection-protocol.h" |  | ||||||
| #include "util/signal.h" |  | ||||||
| 
 |  | ||||||
| #define DEVICE_MANAGER_VERSION 1 |  | ||||||
| 
 |  | ||||||
| static const struct gtk_primary_selection_offer_interface offer_impl; |  | ||||||
| 
 |  | ||||||
| static struct wlr_gtk_primary_selection_device *device_from_offer_resource( |  | ||||||
| 		struct wl_resource *resource) { |  | ||||||
| 	assert(wl_resource_instance_of(resource, |  | ||||||
| 		>k_primary_selection_offer_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, int32_t fd) { |  | ||||||
| 	struct wlr_gtk_primary_selection_device *device = |  | ||||||
| 		device_from_offer_resource(resource); |  | ||||||
| 	if (device == NULL || device->seat->primary_selection_source == NULL) { |  | ||||||
| 		close(fd); |  | ||||||
| 		return; |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	wlr_primary_selection_source_send(device->seat->primary_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 gtk_primary_selection_offer_interface offer_impl = { |  | ||||||
| 	.receive = offer_handle_receive, |  | ||||||
| 	.destroy = offer_handle_destroy, |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| static void offer_handle_resource_destroy(struct wl_resource *resource) { |  | ||||||
| 	wl_list_remove(wl_resource_get_link(resource)); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static struct wlr_gtk_primary_selection_device *device_from_resource( |  | ||||||
| 	struct wl_resource *resource); |  | ||||||
| 
 |  | ||||||
| static void create_offer(struct wl_resource *device_resource, |  | ||||||
| 		struct wlr_primary_selection_source *source) { |  | ||||||
| 	struct wlr_gtk_primary_selection_device *device = |  | ||||||
| 		device_from_resource(device_resource); |  | ||||||
| 	assert(device != NULL); |  | ||||||
| 
 |  | ||||||
| 	struct wl_client *client = wl_resource_get_client(device_resource); |  | ||||||
| 	uint32_t version = wl_resource_get_version(device_resource); |  | ||||||
| 	struct wl_resource *resource = wl_resource_create(client, |  | ||||||
| 		>k_primary_selection_offer_interface, version, 0); |  | ||||||
| 	if (resource == NULL) { |  | ||||||
| 		wl_resource_post_no_memory(device_resource); |  | ||||||
| 		return; |  | ||||||
| 	} |  | ||||||
| 	wl_resource_set_implementation(resource, &offer_impl, device, |  | ||||||
| 		offer_handle_resource_destroy); |  | ||||||
| 
 |  | ||||||
| 	wl_list_insert(&device->offers, wl_resource_get_link(resource)); |  | ||||||
| 
 |  | ||||||
| 	gtk_primary_selection_device_send_data_offer(device_resource, resource); |  | ||||||
| 
 |  | ||||||
| 	char **p; |  | ||||||
| 	wl_array_for_each(p, &source->mime_types) { |  | ||||||
| 		gtk_primary_selection_offer_send_offer(resource, *p); |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	gtk_primary_selection_device_send_selection(device_resource, resource); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static void destroy_offer(struct wl_resource *resource) { |  | ||||||
| 	if (device_from_offer_resource(resource) == NULL) { |  | ||||||
| 		return; |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	// Make the offer inert
 |  | ||||||
| 	wl_resource_set_user_data(resource, NULL); |  | ||||||
| 
 |  | ||||||
| 	struct wl_list *link = wl_resource_get_link(resource); |  | ||||||
| 	wl_list_remove(link); |  | ||||||
| 	wl_list_init(link); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| struct client_data_source { |  | ||||||
| 	struct wlr_primary_selection_source source; |  | ||||||
| 	struct wl_resource *resource; |  | ||||||
| 	bool finalized; |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| static void client_source_send( |  | ||||||
| 		struct wlr_primary_selection_source *wlr_source, |  | ||||||
| 		const char *mime_type, int fd) { |  | ||||||
| 	struct client_data_source *source = (struct client_data_source *)wlr_source; |  | ||||||
| 	gtk_primary_selection_source_send_send(source->resource, mime_type, fd); |  | ||||||
| 	close(fd); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static void client_source_destroy( |  | ||||||
| 		struct wlr_primary_selection_source *wlr_source) { |  | ||||||
| 	struct client_data_source *source = (struct client_data_source *)wlr_source; |  | ||||||
| 	gtk_primary_selection_source_send_cancelled(source->resource); |  | ||||||
| 	// Make the source resource inert
 |  | ||||||
| 	wl_resource_set_user_data(source->resource, NULL); |  | ||||||
| 	free(source); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static const struct wlr_primary_selection_source_impl client_source_impl = { |  | ||||||
| 	.send = client_source_send, |  | ||||||
| 	.destroy = client_source_destroy, |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| static const struct gtk_primary_selection_source_interface source_impl; |  | ||||||
| 
 |  | ||||||
| static struct client_data_source *client_data_source_from_resource( |  | ||||||
| 		struct wl_resource *resource) { |  | ||||||
| 	assert(wl_resource_instance_of(resource, |  | ||||||
| 		>k_primary_selection_source_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 = |  | ||||||
| 		client_data_source_from_resource(resource); |  | ||||||
| 	if (source == NULL) { |  | ||||||
| 		return; |  | ||||||
| 	} |  | ||||||
| 	if (source->finalized) { |  | ||||||
| 		wlr_log(WLR_DEBUG, "Offering additional MIME type after set_selection"); |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	const char **mime_type_ptr; |  | ||||||
| 	wl_array_for_each(mime_type_ptr, &source->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->source.mime_types, sizeof(*p)); |  | ||||||
| 	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 gtk_primary_selection_source_interface source_impl = { |  | ||||||
| 	.offer = source_handle_offer, |  | ||||||
| 	.destroy = source_handle_destroy, |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| static void source_resource_handle_destroy(struct wl_resource *resource) { |  | ||||||
| 	struct client_data_source *source = |  | ||||||
| 		client_data_source_from_resource(resource); |  | ||||||
| 	if (source == NULL) { |  | ||||||
| 		return; |  | ||||||
| 	} |  | ||||||
| 	wlr_primary_selection_source_destroy(&source->source); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| static const struct gtk_primary_selection_device_interface device_impl; |  | ||||||
| 
 |  | ||||||
| static struct wlr_gtk_primary_selection_device *device_from_resource( |  | ||||||
| 		struct wl_resource *resource) { |  | ||||||
| 	assert(wl_resource_instance_of(resource, |  | ||||||
| 		>k_primary_selection_device_interface, &device_impl)); |  | ||||||
| 	return wl_resource_get_user_data(resource); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static void device_handle_set_selection(struct wl_client *client, |  | ||||||
| 		struct wl_resource *resource, struct wl_resource *source_resource, |  | ||||||
| 		uint32_t serial) { |  | ||||||
| 	struct wlr_gtk_primary_selection_device *device = |  | ||||||
| 		device_from_resource(resource); |  | ||||||
| 	if (device == NULL) { |  | ||||||
| 		return; |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	struct client_data_source *client_source = NULL; |  | ||||||
| 	if (source_resource != NULL) { |  | ||||||
| 		client_source = client_data_source_from_resource(source_resource); |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	struct wlr_primary_selection_source *source = NULL; |  | ||||||
| 	if (client_source != NULL) { |  | ||||||
| 		client_source->finalized = true; |  | ||||||
| 		source = &client_source->source; |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	struct wlr_seat_client *seat_client = |  | ||||||
| 		wlr_seat_client_for_wl_client(device->seat, client); |  | ||||||
| 
 |  | ||||||
| 	wlr_seat_request_set_primary_selection(device->seat, seat_client, source, serial); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static void device_handle_destroy(struct wl_client *client, |  | ||||||
| 		struct wl_resource *resource) { |  | ||||||
| 	wl_resource_destroy(resource); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static const struct gtk_primary_selection_device_interface device_impl = { |  | ||||||
| 	.set_selection = device_handle_set_selection, |  | ||||||
| 	.destroy = device_handle_destroy, |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| static void device_handle_resource_destroy(struct wl_resource *resource) { |  | ||||||
| 	wl_list_remove(wl_resource_get_link(resource)); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| static void device_resource_send_selection(struct wl_resource *resource, |  | ||||||
| 		struct wlr_primary_selection_source *source) { |  | ||||||
| 	assert(device_from_resource(resource) != NULL); |  | ||||||
| 
 |  | ||||||
| 	if (source != NULL) { |  | ||||||
| 		create_offer(resource, source); |  | ||||||
| 	} else { |  | ||||||
| 		gtk_primary_selection_device_send_selection(resource, NULL); |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static void device_send_selection( |  | ||||||
| 		struct wlr_gtk_primary_selection_device *device) { |  | ||||||
| 	struct wlr_seat_client *seat_client = |  | ||||||
| 		device->seat->keyboard_state.focused_client; |  | ||||||
| 	if (seat_client == NULL) { |  | ||||||
| 		return; |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	struct wl_resource *resource; |  | ||||||
| 	wl_resource_for_each(resource, &device->resources) { |  | ||||||
| 		if (wl_resource_get_client(resource) == seat_client->client) { |  | ||||||
| 			device_resource_send_selection(resource, |  | ||||||
| 				device->seat->primary_selection_source); |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static void device_destroy(struct wlr_gtk_primary_selection_device *device); |  | ||||||
| 
 |  | ||||||
| static void device_handle_seat_destroy(struct wl_listener *listener, |  | ||||||
| 		void *data) { |  | ||||||
| 	struct wlr_gtk_primary_selection_device *device = |  | ||||||
| 		wl_container_of(listener, device, seat_destroy); |  | ||||||
| 	device_destroy(device); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static void device_handle_seat_focus_change(struct wl_listener *listener, |  | ||||||
| 		void *data) { |  | ||||||
| 	struct wlr_gtk_primary_selection_device *device = |  | ||||||
| 		wl_container_of(listener, device, seat_focus_change); |  | ||||||
| 	// TODO: maybe make previous offers inert, or set a NULL selection for
 |  | ||||||
| 	// previous client?
 |  | ||||||
| 	device_send_selection(device); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static void device_handle_seat_set_primary_selection( |  | ||||||
| 		struct wl_listener *listener, void *data) { |  | ||||||
| 	struct wlr_gtk_primary_selection_device *device = |  | ||||||
| 		wl_container_of(listener, device, seat_set_primary_selection); |  | ||||||
| 
 |  | ||||||
| 	struct wl_resource *resource, *tmp; |  | ||||||
| 	wl_resource_for_each_safe(resource, tmp, &device->offers) { |  | ||||||
| 		destroy_offer(resource); |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	device_send_selection(device); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static struct wlr_gtk_primary_selection_device *get_or_create_device( |  | ||||||
| 		struct wlr_gtk_primary_selection_device_manager *manager, |  | ||||||
| 		struct wlr_seat *seat) { |  | ||||||
| 	struct wlr_gtk_primary_selection_device *device; |  | ||||||
| 	wl_list_for_each(device, &manager->devices, link) { |  | ||||||
| 		if (device->seat == seat) { |  | ||||||
| 			return device; |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	device = calloc(1, sizeof(struct wlr_gtk_primary_selection_device)); |  | ||||||
| 	if (device == NULL) { |  | ||||||
| 		return NULL; |  | ||||||
| 	} |  | ||||||
| 	device->manager = manager; |  | ||||||
| 	device->seat = seat; |  | ||||||
| 
 |  | ||||||
| 	wl_list_init(&device->resources); |  | ||||||
| 	wl_list_insert(&manager->devices, &device->link); |  | ||||||
| 
 |  | ||||||
| 	wl_list_init(&device->offers); |  | ||||||
| 
 |  | ||||||
| 	device->seat_destroy.notify = device_handle_seat_destroy; |  | ||||||
| 	wl_signal_add(&seat->events.destroy, &device->seat_destroy); |  | ||||||
| 
 |  | ||||||
| 	device->seat_focus_change.notify = device_handle_seat_focus_change; |  | ||||||
| 	wl_signal_add(&seat->keyboard_state.events.focus_change, |  | ||||||
| 		&device->seat_focus_change); |  | ||||||
| 
 |  | ||||||
| 	device->seat_set_primary_selection.notify = |  | ||||||
| 		device_handle_seat_set_primary_selection; |  | ||||||
| 	wl_signal_add(&seat->events.set_primary_selection, |  | ||||||
| 		&device->seat_set_primary_selection); |  | ||||||
| 
 |  | ||||||
| 	return device; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static void device_destroy(struct wlr_gtk_primary_selection_device *device) { |  | ||||||
| 	if (device == NULL) { |  | ||||||
| 		return; |  | ||||||
| 	} |  | ||||||
| 	wl_list_remove(&device->link); |  | ||||||
| 	wl_list_remove(&device->seat_destroy.link); |  | ||||||
| 	wl_list_remove(&device->seat_focus_change.link); |  | ||||||
| 	wl_list_remove(&device->seat_set_primary_selection.link); |  | ||||||
| 	struct wl_resource *resource, *resource_tmp; |  | ||||||
| 	wl_resource_for_each_safe(resource, resource_tmp, &device->offers) { |  | ||||||
| 		destroy_offer(resource); |  | ||||||
| 	} |  | ||||||
| 	wl_resource_for_each_safe(resource, resource_tmp, &device->resources) { |  | ||||||
| 		// Make the resource inert
 |  | ||||||
| 		wl_resource_set_user_data(resource, NULL); |  | ||||||
| 
 |  | ||||||
| 		struct wl_list *link = wl_resource_get_link(resource); |  | ||||||
| 		wl_list_remove(link); |  | ||||||
| 		wl_list_init(link); |  | ||||||
| 	} |  | ||||||
| 	free(device); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| static const struct gtk_primary_selection_device_manager_interface |  | ||||||
| 	device_manager_impl; |  | ||||||
| 
 |  | ||||||
| static struct wlr_gtk_primary_selection_device_manager *manager_from_resource( |  | ||||||
| 		struct wl_resource *resource) { |  | ||||||
| 	assert(wl_resource_instance_of(resource, |  | ||||||
| 		>k_primary_selection_device_manager_interface, &device_manager_impl)); |  | ||||||
| 	return wl_resource_get_user_data(resource); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static void device_manager_handle_create_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_client_post_no_memory(client); |  | ||||||
| 		return; |  | ||||||
| 	} |  | ||||||
| 	wlr_primary_selection_source_init(&source->source, &client_source_impl); |  | ||||||
| 
 |  | ||||||
| 	uint32_t version = wl_resource_get_version(manager_resource); |  | ||||||
| 	source->resource = wl_resource_create(client, |  | ||||||
| 		>k_primary_selection_source_interface, version, id); |  | ||||||
| 	if (source->resource == NULL) { |  | ||||||
| 		free(source); |  | ||||||
| 		wl_client_post_no_memory(client); |  | ||||||
| 		return; |  | ||||||
| 	} |  | ||||||
| 	wl_resource_set_implementation(source->resource, &source_impl, source, |  | ||||||
| 		source_resource_handle_destroy); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static void device_manager_handle_get_device(struct wl_client *client, |  | ||||||
| 		struct wl_resource *manager_resource, uint32_t id, |  | ||||||
| 		struct wl_resource *seat_resource) { |  | ||||||
| 	struct wlr_seat_client *seat_client = |  | ||||||
| 		wlr_seat_client_from_resource(seat_resource); |  | ||||||
| 	struct wlr_gtk_primary_selection_device_manager *manager = |  | ||||||
| 		manager_from_resource(manager_resource); |  | ||||||
| 
 |  | ||||||
| 	struct wlr_gtk_primary_selection_device *device = |  | ||||||
| 		get_or_create_device(manager, seat_client->seat); |  | ||||||
| 	if (device == NULL) { |  | ||||||
| 		wl_resource_post_no_memory(manager_resource); |  | ||||||
| 		return; |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	uint32_t version = wl_resource_get_version(manager_resource); |  | ||||||
| 	struct wl_resource *resource = wl_resource_create(client, |  | ||||||
| 		>k_primary_selection_device_interface, version, id); |  | ||||||
| 	if (resource == NULL) { |  | ||||||
| 		wl_resource_post_no_memory(manager_resource); |  | ||||||
| 		return; |  | ||||||
| 	} |  | ||||||
| 	wl_resource_set_implementation(resource, &device_impl, device, |  | ||||||
| 		device_handle_resource_destroy); |  | ||||||
| 	wl_list_insert(&device->resources, wl_resource_get_link(resource)); |  | ||||||
| 
 |  | ||||||
| 	if (device->seat->keyboard_state.focused_client == seat_client) { |  | ||||||
| 		device_resource_send_selection(resource, |  | ||||||
| 			device->seat->primary_selection_source); |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static void device_manager_handle_destroy(struct wl_client *client, |  | ||||||
| 		struct wl_resource *manager_resource) { |  | ||||||
| 	wl_resource_destroy(manager_resource); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static const struct gtk_primary_selection_device_manager_interface |  | ||||||
| 		device_manager_impl = { |  | ||||||
| 	.create_source = device_manager_handle_create_source, |  | ||||||
| 	.get_device = device_manager_handle_get_device, |  | ||||||
| 	.destroy = device_manager_handle_destroy, |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| static void primary_selection_device_manager_bind(struct wl_client *client, |  | ||||||
| 		void *data, uint32_t version, uint32_t id) { |  | ||||||
| 	struct wlr_gtk_primary_selection_device_manager *manager = data; |  | ||||||
| 
 |  | ||||||
| 	struct wl_resource *resource = wl_resource_create(client, |  | ||||||
| 		>k_primary_selection_device_manager_interface, version, id); |  | ||||||
| 	if (resource == NULL) { |  | ||||||
| 		wl_client_post_no_memory(client); |  | ||||||
| 		return; |  | ||||||
| 	} |  | ||||||
| 	wl_resource_set_implementation(resource, &device_manager_impl, manager, |  | ||||||
| 		NULL); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static void handle_display_destroy(struct wl_listener *listener, void *data) { |  | ||||||
| 	struct wlr_gtk_primary_selection_device_manager *manager = |  | ||||||
| 		wl_container_of(listener, manager, display_destroy); |  | ||||||
| 
 |  | ||||||
| 	struct wlr_gtk_primary_selection_device *device, *tmp; |  | ||||||
| 	wl_list_for_each_safe(device, tmp, &manager->devices, link) { |  | ||||||
| 		device_destroy(device); |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	wlr_signal_emit_safe(&manager->events.destroy, manager); |  | ||||||
| 	wl_list_remove(&manager->display_destroy.link); |  | ||||||
| 	wl_global_destroy(manager->global); |  | ||||||
| 	free(manager); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| struct wlr_gtk_primary_selection_device_manager * |  | ||||||
| 		wlr_gtk_primary_selection_device_manager_create( |  | ||||||
| 		struct wl_display *display) { |  | ||||||
| 	struct wlr_gtk_primary_selection_device_manager *manager = |  | ||||||
| 		calloc(1, sizeof(struct wlr_gtk_primary_selection_device_manager)); |  | ||||||
| 	if (manager == NULL) { |  | ||||||
| 		return NULL; |  | ||||||
| 	} |  | ||||||
| 	manager->global = wl_global_create(display, |  | ||||||
| 		>k_primary_selection_device_manager_interface, DEVICE_MANAGER_VERSION, |  | ||||||
| 		manager, primary_selection_device_manager_bind); |  | ||||||
| 	if (manager->global == NULL) { |  | ||||||
| 		free(manager); |  | ||||||
| 		return NULL; |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	wl_list_init(&manager->devices); |  | ||||||
| 	wl_signal_init(&manager->events.destroy); |  | ||||||
| 
 |  | ||||||
| 	manager->display_destroy.notify = handle_display_destroy; |  | ||||||
| 	wl_display_add_destroy_listener(display, &manager->display_destroy); |  | ||||||
| 
 |  | ||||||
| 	return manager; |  | ||||||
| } |  | ||||||
					Loading…
					
					
				
		Reference in new issue