Merge branch 'clipboard' into 'master'

selection: allows using a wrapper source to filter data

See merge request wlroots/wlroots!5153
This commit is contained in:
JiDe Zhang 2026-02-02 23:54:17 +08:00
commit d1526c3a10
18 changed files with 2723 additions and 108 deletions

View file

@ -11,6 +11,7 @@
#include <wayland-server-core.h>
#include <wlr/types/wlr_seat.h>
#include <wlr/types/wlr_data_receiver.h>
struct wlr_data_device_manager {
struct wl_global *global;
@ -42,6 +43,9 @@ struct wlr_data_offer {
enum wl_data_device_manager_dnd_action preferred_action;
bool in_ask;
/* Data receiver for this offer */
struct wlr_data_receiver receiver;
struct {
struct wl_listener source_destroy;
} WLR_PRIVATE;
@ -53,15 +57,22 @@ struct wlr_data_offer {
*/
struct wlr_data_source_impl {
void (*send)(struct wlr_data_source *source, const char *mime_type,
int32_t fd);
struct wlr_data_receiver *receiver);
void (*accept)(struct wlr_data_source *source, uint32_t serial,
const char *mime_type);
const char *mime_type, struct wlr_data_receiver *receiver);
void (*cancelled)(struct wlr_data_source *source);
void (*destroy)(struct wlr_data_source *source);
void (*dnd_drop)(struct wlr_data_source *source);
void (*dnd_finish)(struct wlr_data_source *source);
void (*dnd_action)(struct wlr_data_source *source,
enum wl_data_device_manager_dnd_action action);
/**
* Returns the unwrapped source object. This source object maybe is a
* wrapper, its a proxy for the others source.
*/
struct wlr_data_source *(*get_original)(struct wlr_data_source *source);
};
struct wlr_data_source {
@ -74,6 +85,10 @@ struct wlr_data_source {
// source status
bool accepted;
// source information
struct wl_client *client;
pid_t pid; // PID of the source process (for XWayland, X11 client PID)
// drag'n'drop status
enum wl_data_device_manager_dnd_action current_dnd_action;
uint32_t compositor_action;
@ -217,18 +232,23 @@ void wlr_data_source_init(struct wlr_data_source *source,
const struct wlr_data_source_impl *impl);
/**
* Sends the data as the specified MIME type over the passed file descriptor,
* then close it.
* Sends the data as the specified MIME type to the receiver,
* with receiver managing the file descriptor and callbacks.
*/
void wlr_data_source_send(struct wlr_data_source *source, const char *mime_type,
int32_t fd);
void wlr_data_source_send(struct wlr_data_source *source,
const char *mime_type, struct wlr_data_receiver *receiver);
/**
* Notifies the data source that a target accepts one of the offered MIME types.
* If a target doesn't accept any of the offered types, `mime_type` is NULL.
*/
void wlr_data_source_accept(struct wlr_data_source *source, uint32_t serial,
const char *mime_type);
void wlr_data_source_accept(struct wlr_data_source *source,
uint32_t serial, const char *mime_type, struct wlr_data_receiver *receiver);
/**
* Notifies the data source that the operation was cancelled.
*/
void wlr_data_source_cancelled(struct wlr_data_source *source);
/**
* Notifies the data source it is no longer valid and should be destroyed. That
@ -261,4 +281,17 @@ void wlr_data_source_dnd_finish(struct wlr_data_source *source);
void wlr_data_source_dnd_action(struct wlr_data_source *source,
enum wl_data_device_manager_dnd_action action);
/**
* Copy metadata from one data source to another. This is useful for implementing
* wrapper data sources that can filter MIME types or other metadata.
*/
void wlr_data_source_copy(struct wlr_data_source *dest, struct wlr_data_source *src);
/**
* Returns the original source object, it isn't NULL if the source argument
* isn't NULL.
*/
struct wlr_data_source *
wlr_data_source_get_original(struct wlr_data_source *source);
#endif

View file

@ -0,0 +1,95 @@
/*
* 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_RECEIVER_H
#define WLR_TYPES_WLR_DATA_RECEIVER_H
#include <wayland-server-core.h>
#include <unistd.h>
struct wlr_data_receiver;
/**
* A data receiver implementation. All callbacks are optional.
*/
struct wlr_data_receiver_impl {
/**
* Called when the transfer is cancelled before completion. This should
* clean up any ongoing transfer state. The fd will be automatically
* closed after this callback returns.
*/
void (*cancelled)(struct wlr_data_receiver *receiver);
/**
* Called when the receiver is being destroyed. This should free any
* resources allocated for the receiver implementation.
*/
void (*destroy)(struct wlr_data_receiver *receiver);
};
/**
* A receiver is the receiving side of a data transfer. It represents the
* target of clipboard, primary selection, or drag-and-drop operations.
*
* This abstraction unifies the handling of data transfers across different
* protocols (Wayland native, XWayland, etc.) and provides a consistent
* interface for permission systems and transfer tracking.
*/
struct wlr_data_receiver {
const struct wlr_data_receiver_impl *impl;
/**
* File descriptor for data transfer. This is typically a pipe write end
* that will be passed to the data source for writing clipboard data.
*/
int fd;
/**
* Process ID of the receiving client. For XWayland windows, this is the
* X11 client PID, not the XWayland server PID. For native Wayland clients,
* this is the client process PID.
*/
pid_t pid;
/**
* The Wayland client associated with this receiver. For XWayland windows,
* this refers to the XWayland server client, not the X11 application.
* For native Wayland clients, this is the actual client.
*/
struct wl_client *client;
struct {
struct wl_signal destroy;
} events;
};
/**
* Initialize a data receiver with the given implementation. The receiver
* should be embedded in a larger structure (e.g., wlr_data_offer) to manage
* its lifecycle properly.
*/
void wlr_data_receiver_init(struct wlr_data_receiver *receiver,
const struct wlr_data_receiver_impl *impl);
/**
* Destroy a data receiver. This will emit the destroy signal, call the
* implementation's destroy callback if present, and close any open file
* descriptor. After calling this function, the receiver should not be used.
*/
void wlr_data_receiver_destroy(struct wlr_data_receiver *receiver);
/**
* Notify the receiver that the transfer has been cancelled. This calls the
* cancelled callback if implemented, then automatically closes the file
* descriptor. Should be called when a transfer is aborted before completion,
* such as when a drag operation is cancelled or a selection is cleared.
*/
void wlr_data_receiver_cancelled(struct wlr_data_receiver *receiver);
#endif

View file

@ -11,6 +11,7 @@
#include <wayland-server-core.h>
#include <wlr/types/wlr_seat.h>
#include <wlr/types/wlr_data_receiver.h>
struct wlr_primary_selection_source;
@ -19,8 +20,14 @@ struct wlr_primary_selection_source;
*/
struct wlr_primary_selection_source_impl {
void (*send)(struct wlr_primary_selection_source *source,
const char *mime_type, int fd);
const char *mime_type, struct wlr_data_receiver *receiver);
void (*destroy)(struct wlr_primary_selection_source *source);
/**
* Returns the unwrapped source object. This source object maybe is a
* wrapper, its a proxy for the others source.
*/
struct wlr_primary_selection_source *(*get_original)(struct wlr_primary_selection_source *source);
};
/**
@ -32,6 +39,10 @@ struct wlr_primary_selection_source {
// source metadata
struct wl_array mime_types;
// source information
struct wl_client *client;
pid_t pid; // PID of the source process (for XWayland, X11 client PID)
struct {
struct wl_signal destroy;
} events;
@ -45,8 +56,22 @@ void wlr_primary_selection_source_init(
void wlr_primary_selection_source_destroy(
struct wlr_primary_selection_source *source);
void wlr_primary_selection_source_send(
struct wlr_primary_selection_source *source, const char *mime_type,
int fd);
struct wlr_primary_selection_source *source,
const char *mime_type, struct wlr_data_receiver *receiver);
/**
* Copy metadata from one primary selection source to another. This is useful for implementing
* wrapper primary selection sources that can filter MIME types or other metadata.
*/
void wlr_primary_selection_source_copy(struct wlr_primary_selection_source *dest,
struct wlr_primary_selection_source *src);
/**
* Returns the original primary selection source object, it isn't NULL if the source argument
* isn't NULL.
*/
struct wlr_primary_selection_source *
wlr_primary_selection_source_get_original(struct wlr_primary_selection_source *source);
/**
* Request setting the primary selection. If `client` is not null, then the

View file

@ -4,6 +4,7 @@
#include <stdbool.h>
#include <xcb/xfixes.h>
#include <wayland-util.h>
#include <wlr/types/wlr_data_receiver.h>
#define INCR_CHUNK_SIZE (64 * 1024)
@ -34,6 +35,12 @@ struct wlr_xwm_selection_transfer {
int property_start;
xcb_get_property_reply_t *property_reply;
xcb_window_t incoming_window;
// Data receiver reference for Wayland client (may be NULL)
struct wlr_data_receiver *wl_client_receiver;
// Listener for receiver destruction
struct wl_listener receiver_destroy;
};
struct wlr_xwm_selection {
@ -60,7 +67,7 @@ void xwm_selection_transfer_destroy_property_reply(
struct wlr_xwm_selection_transfer *transfer);
void xwm_selection_transfer_init(struct wlr_xwm_selection_transfer *transfer,
struct wlr_xwm_selection *selection);
void xwm_selection_transfer_destroy(
void xwm_selection_transfer_destroy_incoming(
struct wlr_xwm_selection_transfer *transfer);
void xwm_selection_transfer_destroy_outgoing(
@ -88,6 +95,8 @@ bool primary_selection_source_is_xwayland(
void xwm_seat_handle_start_drag(struct wlr_xwm *xwm, struct wlr_drag *drag);
pid_t get_x11_window_pid(xcb_connection_t *conn, xcb_window_t window);
void xwm_selection_init(struct wlr_xwm_selection *selection,
struct wlr_xwm *xwm, xcb_atom_t atom);
void xwm_selection_finish(struct wlr_xwm_selection *selection);