migrate clipboard bridge from wlr-data-control to ext-data-control
The wlr-data-control unstable protocol is deprecated; its superseding staging protocol ext-data-control-v1 ships in wayland-protocols. Migrate the host-side clipboard/primary bridge to it: - bind ext_data_control_manager_v1 version 1 instead of zwlr_manager (ext-data-control v1 always supports primary selection, so the dc_primary_supported gate is dropped and the registry bind is plain) - rename all zwlr_data_control_* to ext_data_control_* types/calls - generate ext-data-control-v1 from wayland-protocols and drop the bundled wlr-data-control-unstable-v1.xml - update README and test log-message expectations wltest --screencopy is unaffected; the screencopy migration is separate.
This commit is contained in:
parent
0dc782a5d3
commit
592a2a036e
5 changed files with 98 additions and 383 deletions
14
README.md
14
README.md
|
|
@ -20,13 +20,13 @@ meson compile -C build
|
|||
The resulting binary is `build/kick`.
|
||||
|
||||
The Wayland protocol stubs (`xdg-shell`, `xdg-decoration`,
|
||||
`cursor-shape-v1` and `tablet-v2`, `wlr-data-control-unstable-v1`) are not
|
||||
committed; they are generated into the build directory by `wayland-scanner`
|
||||
from the system `wayland-protocols` (and the bundled
|
||||
`wlr-data-control-unstable-v1.xml`), so they always match the installed
|
||||
protocol headers. This adds no extra dependency beyond what the wlroots
|
||||
fallback already requires. (`tablet-v2` is generated only because
|
||||
cursor-shape's implementation references `wl_tablet_tool_v2_interface`.)
|
||||
`cursor-shape-v1` and `tablet-v2`, `ext-data-control-v1`,
|
||||
`ext-image-copy-capture-v1`) are not committed; they are generated into the
|
||||
build directory by `wayland-scanner` from the system `wayland-protocols`
|
||||
(and the bundled `wlr-layer-shell-unstable-v1.xml`), so they always match
|
||||
the installed protocol headers. This adds no extra dependency beyond what
|
||||
the wlroots fallback already requires. (`tablet-v2` is generated only
|
||||
because cursor-shape's implementation references `wl_tablet_tool_v2_interface`.)
|
||||
|
||||
### Debian / Ubuntu
|
||||
|
||||
|
|
|
|||
165
kick.c
165
kick.c
|
|
@ -38,7 +38,7 @@
|
|||
|
||||
#include "xdg-shell-client.h"
|
||||
#include "xdg-decoration-client.h"
|
||||
#include "wlr-data-control-client.h"
|
||||
#include "ext-data-control-v1-client.h"
|
||||
|
||||
#include <EGL/egl.h>
|
||||
#include <EGL/eglext.h>
|
||||
|
|
@ -80,7 +80,7 @@
|
|||
#include <wlr/types/wlr_content_type_v1.h>
|
||||
#include <wlr/types/wlr_cursor.h>
|
||||
#include <wlr/types/wlr_cursor_shape_v1.h>
|
||||
#include <wlr/types/wlr_data_control_v1.h>
|
||||
#include <wlr/types/wlr_ext_data_control_v1.h>
|
||||
#include <wlr/types/wlr_data_device.h>
|
||||
#include <wlr/types/wlr_ext_foreign_toplevel_list_v1.h>
|
||||
#include <wlr/types/wlr_foreign_toplevel_management_v1.h>
|
||||
|
|
@ -756,7 +756,7 @@ enum bridge_backend {
|
|||
|
||||
/* Bridges the nested seat's selection with the host compositor's. Two host
|
||||
backends share one inner (nested-seat) implementation:
|
||||
- data-control: zwlr_data_control_v1 (labwc/sway/etc.), full duplex,
|
||||
- data-control: ext_data_control_v1 (labwc/sway/etc.), full duplex,
|
||||
independent of keyboard focus; its v2 set_primary_selection /
|
||||
primary_selection pair carries the primary (middle-click) selection.
|
||||
- wl_data_device: plain stable protocol (Mutter/GNOME fallback). Writing
|
||||
|
|
@ -797,18 +797,17 @@ struct clipboard_bridge {
|
|||
struct wl_data_offer *dd_echo_offer; /* offer describing our own set */
|
||||
uint32_t kbd_serial; /* last host keyboard/enter serial */
|
||||
|
||||
/* host: wlr-data-control backend (if the host exposes it) */
|
||||
struct zwlr_data_control_manager_v1 *dc_manager;
|
||||
struct zwlr_data_control_device_v1 *dc_device;
|
||||
struct zwlr_data_control_source_v1 *dc_source;
|
||||
struct zwlr_data_control_offer_v1 *dc_offer;
|
||||
struct zwlr_data_control_offer_v1 *dc_echo_offer;
|
||||
/* host primary selection over the same data-control device (v2): our
|
||||
published source, the current host offer, and the echo of our own set */
|
||||
struct zwlr_data_control_source_v1 *dc_primary_source;
|
||||
struct zwlr_data_control_offer_v1 *dc_primary_offer;
|
||||
struct zwlr_data_control_offer_v1 *dc_primary_echo_offer;
|
||||
bool dc_primary_supported; /* bound data-control version >= 2 */
|
||||
/* host: ext-data-control backend (if the host exposes it) */
|
||||
struct ext_data_control_manager_v1 *dc_manager;
|
||||
struct ext_data_control_device_v1 *dc_device;
|
||||
struct ext_data_control_source_v1 *dc_source;
|
||||
struct ext_data_control_offer_v1 *dc_offer;
|
||||
struct ext_data_control_offer_v1 *dc_echo_offer;
|
||||
/* host primary selection over the same device: our published source,
|
||||
the current host offer, and the echo of our own set */
|
||||
struct ext_data_control_source_v1 *dc_primary_source;
|
||||
struct ext_data_control_offer_v1 *dc_primary_offer;
|
||||
struct ext_data_control_offer_v1 *dc_primary_echo_offer;
|
||||
|
||||
/* MIME list currently being collected from a host offer */
|
||||
char **mimes;
|
||||
|
|
@ -5393,7 +5392,7 @@ static void bridge_inner_source_send(struct wlr_data_source *src,
|
|||
return;
|
||||
}
|
||||
if (bridge.backend == BRIDGE_BACKEND_DATA_CONTROL && bridge.dc_offer) {
|
||||
zwlr_data_control_offer_v1_receive(bridge.dc_offer, mime, fd);
|
||||
ext_data_control_offer_v1_receive(bridge.dc_offer, mime, fd);
|
||||
close(fd);
|
||||
} else if (bridge.backend == BRIDGE_BACKEND_WL_DATA_DEVICE &&
|
||||
bridge.dd_offer) {
|
||||
|
|
@ -5482,7 +5481,7 @@ static void bridge_inner_primary_source_send(struct wlr_primary_selection_source
|
|||
return;
|
||||
}
|
||||
if (bridge.backend == BRIDGE_BACKEND_DATA_CONTROL && bridge.dc_primary_offer) {
|
||||
zwlr_data_control_offer_v1_receive(bridge.dc_primary_offer, mime, fd);
|
||||
ext_data_control_offer_v1_receive(bridge.dc_primary_offer, mime, fd);
|
||||
close(fd);
|
||||
} else {
|
||||
close(fd);
|
||||
|
|
@ -5671,7 +5670,7 @@ static void bridge_prefetch_start(void) {
|
|||
}
|
||||
snprintf(bridge.prefetch_mime, sizeof(bridge.prefetch_mime), "%s", mime);
|
||||
if (bridge.backend == BRIDGE_BACKEND_DATA_CONTROL && bridge.dc_offer) {
|
||||
zwlr_data_control_offer_v1_receive(bridge.dc_offer, mime, p[1]);
|
||||
ext_data_control_offer_v1_receive(bridge.dc_offer, mime, p[1]);
|
||||
close(p[1]);
|
||||
} else if (bridge.backend == BRIDGE_BACKEND_WL_DATA_DEVICE &&
|
||||
bridge.dd_offer) {
|
||||
|
|
@ -5819,7 +5818,7 @@ static void bridge_primary_prefetch_start(void) {
|
|||
}
|
||||
snprintf(bridge.primary_prefetch_mime, sizeof(bridge.primary_prefetch_mime),
|
||||
"%s", mime);
|
||||
zwlr_data_control_offer_v1_receive(bridge.dc_primary_offer, mime, p[1]);
|
||||
ext_data_control_offer_v1_receive(bridge.dc_primary_offer, mime, p[1]);
|
||||
close(p[1]);
|
||||
}
|
||||
|
||||
|
|
@ -5859,7 +5858,7 @@ static void bridge_primary_prefetch_serve(const char *mime, int32_t fd) {
|
|||
/* ---------------- host backend: wlr-data-control ---------------- */
|
||||
|
||||
static void bridge_dc_source_send(void *data,
|
||||
struct zwlr_data_control_source_v1 *src, const char *mime, int32_t fd) {
|
||||
struct ext_data_control_source_v1 *src, const char *mime, int32_t fd) {
|
||||
(void)data; (void)src;
|
||||
kick_log(KICK_LOG_DEBUG, "[dbg-host] t=%.0f dc_source_send mime=%s "
|
||||
"fd=%d inner_src=%p\n", bridge_dbg_ms(), mime, fd,
|
||||
|
|
@ -5873,16 +5872,16 @@ static void bridge_dc_source_send(void *data,
|
|||
}
|
||||
|
||||
static void bridge_dc_source_cancelled(void *data,
|
||||
struct zwlr_data_control_source_v1 *src) {
|
||||
struct ext_data_control_source_v1 *src) {
|
||||
(void)data;
|
||||
kick_log(KICK_LOG_DEBUG, "[dbg-bridge] host cancelled our data-control source\n");
|
||||
zwlr_data_control_source_v1_destroy(src);
|
||||
ext_data_control_source_v1_destroy(src);
|
||||
bridge.dc_source = NULL;
|
||||
bridge.dc_pending_echo = false;
|
||||
bridge.dd_pending_echo = false;
|
||||
}
|
||||
|
||||
static const struct zwlr_data_control_source_v1_listener bridge_dc_source_listener = {
|
||||
static const struct ext_data_control_source_v1_listener bridge_dc_source_listener = {
|
||||
.send = bridge_dc_source_send,
|
||||
.cancelled = bridge_dc_source_cancelled,
|
||||
};
|
||||
|
|
@ -5890,7 +5889,7 @@ static const struct zwlr_data_control_source_v1_listener bridge_dc_source_listen
|
|||
/* Our published host primary-selection source: host middle-click requests
|
||||
are served by asking the originating nested client for the data. */
|
||||
static void bridge_dc_primary_source_send(void *data,
|
||||
struct zwlr_data_control_source_v1 *src, const char *mime, int32_t fd) {
|
||||
struct ext_data_control_source_v1 *src, const char *mime, int32_t fd) {
|
||||
(void)data; (void)src;
|
||||
if (bridge.inner_primary_src)
|
||||
wlr_primary_selection_source_send(bridge.inner_primary_src, mime, fd);
|
||||
|
|
@ -5899,53 +5898,53 @@ static void bridge_dc_primary_source_send(void *data,
|
|||
}
|
||||
|
||||
static void bridge_dc_primary_source_cancelled(void *data,
|
||||
struct zwlr_data_control_source_v1 *src) {
|
||||
struct ext_data_control_source_v1 *src) {
|
||||
(void)data;
|
||||
zwlr_data_control_source_v1_destroy(src);
|
||||
ext_data_control_source_v1_destroy(src);
|
||||
bridge.dc_primary_source = NULL;
|
||||
bridge.dc_primary_pending_echo = false;
|
||||
}
|
||||
|
||||
static const struct zwlr_data_control_source_v1_listener bridge_dc_primary_source_listener = {
|
||||
static const struct ext_data_control_source_v1_listener bridge_dc_primary_source_listener = {
|
||||
.send = bridge_dc_primary_source_send,
|
||||
.cancelled = bridge_dc_primary_source_cancelled,
|
||||
};
|
||||
|
||||
static void bridge_dc_offer_mime(void *data,
|
||||
struct zwlr_data_control_offer_v1 *offer, const char *mime) {
|
||||
struct ext_data_control_offer_v1 *offer, const char *mime) {
|
||||
(void)data; (void)offer;
|
||||
bridge_mimes_append(mime);
|
||||
}
|
||||
|
||||
static const struct zwlr_data_control_offer_v1_listener bridge_dc_offer_listener = {
|
||||
static const struct ext_data_control_offer_v1_listener bridge_dc_offer_listener = {
|
||||
.offer = bridge_dc_offer_mime,
|
||||
};
|
||||
|
||||
static void bridge_dc_device_finished(void *data,
|
||||
struct zwlr_data_control_device_v1 *dev) {
|
||||
struct ext_data_control_device_v1 *dev) {
|
||||
(void)data; (void)dev;
|
||||
bridge.backend = bridge.dd ? BRIDGE_BACKEND_WL_DATA_DEVICE
|
||||
: BRIDGE_BACKEND_NONE;
|
||||
if (bridge.dc_offer) {
|
||||
zwlr_data_control_offer_v1_destroy(bridge.dc_offer);
|
||||
ext_data_control_offer_v1_destroy(bridge.dc_offer);
|
||||
bridge.dc_offer = NULL;
|
||||
}
|
||||
if (bridge.dc_primary_offer) {
|
||||
zwlr_data_control_offer_v1_destroy(bridge.dc_primary_offer);
|
||||
ext_data_control_offer_v1_destroy(bridge.dc_primary_offer);
|
||||
bridge.dc_primary_offer = NULL;
|
||||
}
|
||||
if (bridge.dc_primary_echo_offer) {
|
||||
zwlr_data_control_offer_v1_destroy(bridge.dc_primary_echo_offer);
|
||||
ext_data_control_offer_v1_destroy(bridge.dc_primary_echo_offer);
|
||||
bridge.dc_primary_echo_offer = NULL;
|
||||
}
|
||||
if (bridge.dc_primary_source) {
|
||||
zwlr_data_control_source_v1_destroy(bridge.dc_primary_source);
|
||||
ext_data_control_source_v1_destroy(bridge.dc_primary_source);
|
||||
bridge.dc_primary_source = NULL;
|
||||
}
|
||||
bridge.dc_primary_pending_echo = false;
|
||||
bridge_primary_prefetch_cancel();
|
||||
if (bridge.dc_device) {
|
||||
zwlr_data_control_device_v1_destroy(bridge.dc_device);
|
||||
ext_data_control_device_v1_destroy(bridge.dc_device);
|
||||
bridge.dc_device = NULL;
|
||||
}
|
||||
kick_log(KICK_LOG_INFO, "clipboard bridge: host data-control "
|
||||
|
|
@ -5955,8 +5954,8 @@ static void bridge_dc_device_finished(void *data,
|
|||
}
|
||||
|
||||
static void bridge_dc_device_data_offer(void *data,
|
||||
struct zwlr_data_control_device_v1 *dev,
|
||||
struct zwlr_data_control_offer_v1 *offer) {
|
||||
struct ext_data_control_device_v1 *dev,
|
||||
struct ext_data_control_offer_v1 *offer) {
|
||||
(void)data; (void)dev;
|
||||
kick_log(KICK_LOG_DEBUG, "[dbg-host] t=%.0f dc data_offer %p pending_echo=%d "
|
||||
"primary_pending=%d\n", bridge_dbg_ms(), (void *)offer,
|
||||
|
|
@ -5969,12 +5968,12 @@ static void bridge_dc_device_data_offer(void *data,
|
|||
bridge.dc_primary_echo_offer = offer;
|
||||
}
|
||||
bridge_mimes_reset();
|
||||
zwlr_data_control_offer_v1_add_listener(offer, &bridge_dc_offer_listener, NULL);
|
||||
ext_data_control_offer_v1_add_listener(offer, &bridge_dc_offer_listener, NULL);
|
||||
}
|
||||
|
||||
static void bridge_dc_device_selection(void *data,
|
||||
struct zwlr_data_control_device_v1 *dev,
|
||||
struct zwlr_data_control_offer_v1 *offer) {
|
||||
struct ext_data_control_device_v1 *dev,
|
||||
struct ext_data_control_offer_v1 *offer) {
|
||||
(void)data; (void)dev;
|
||||
kick_log(KICK_LOG_DEBUG, "[dbg-host] t=%.0f dc selection offer=%p pending_echo=%d "
|
||||
"echo_offer=%p\n", bridge_dbg_ms(), (void *)offer,
|
||||
|
|
@ -5988,14 +5987,14 @@ static void bridge_dc_device_selection(void *data,
|
|||
if (offer && offer == bridge.dc_echo_offer) {
|
||||
bridge.dc_echo_offer = NULL;
|
||||
bridge_mimes_reset();
|
||||
zwlr_data_control_offer_v1_destroy(offer);
|
||||
ext_data_control_offer_v1_destroy(offer);
|
||||
bridge.dc_pending_echo = false;
|
||||
return;
|
||||
}
|
||||
bridge.dc_pending_echo = false;
|
||||
bridge.dd_pending_echo = false;
|
||||
if (bridge.dc_offer)
|
||||
zwlr_data_control_offer_v1_destroy(bridge.dc_offer);
|
||||
ext_data_control_offer_v1_destroy(bridge.dc_offer);
|
||||
bridge.dc_offer = offer;
|
||||
if (!offer) {
|
||||
bridge_mimes_reset();
|
||||
|
|
@ -6010,14 +6009,14 @@ static void bridge_dc_device_selection(void *data,
|
|||
host's primary_selection event mirrors the host's middle-click selection
|
||||
into the nested seat (and the echo of our own set is swallowed). */
|
||||
static void bridge_dc_device_primary_selection(void *data,
|
||||
struct zwlr_data_control_device_v1 *dev,
|
||||
struct zwlr_data_control_offer_v1 *offer) {
|
||||
struct ext_data_control_device_v1 *dev,
|
||||
struct ext_data_control_offer_v1 *offer) {
|
||||
(void)data; (void)dev;
|
||||
if (offer && offer == bridge.dc_primary_echo_offer) {
|
||||
bridge.dc_primary_echo_offer = NULL;
|
||||
bridge.dc_primary_pending_echo = false;
|
||||
bridge_mimes_reset();
|
||||
zwlr_data_control_offer_v1_destroy(offer);
|
||||
ext_data_control_offer_v1_destroy(offer);
|
||||
kick_log(KICK_LOG_DEBUG, "[bridge] primary echo swallowed\n");
|
||||
return;
|
||||
}
|
||||
|
|
@ -6036,7 +6035,7 @@ static void bridge_dc_device_primary_selection(void *data,
|
|||
}
|
||||
bridge.dc_primary_pending_echo = false;
|
||||
if (bridge.dc_primary_offer)
|
||||
zwlr_data_control_offer_v1_destroy(bridge.dc_primary_offer);
|
||||
ext_data_control_offer_v1_destroy(bridge.dc_primary_offer);
|
||||
bridge.dc_primary_offer = offer;
|
||||
kick_log(KICK_LOG_DEBUG, "[bridge] host primary_selection offer\n");
|
||||
/* The offer's mimes were collected by the preceding data_offer/offer
|
||||
|
|
@ -6045,7 +6044,7 @@ static void bridge_dc_device_primary_selection(void *data,
|
|||
bridge_mirror_primary_to_nested();
|
||||
}
|
||||
|
||||
static const struct zwlr_data_control_device_v1_listener bridge_dc_listener = {
|
||||
static const struct ext_data_control_device_v1_listener bridge_dc_listener = {
|
||||
.data_offer = bridge_dc_device_data_offer,
|
||||
.selection = bridge_dc_device_selection,
|
||||
.primary_selection = bridge_dc_device_primary_selection,
|
||||
|
|
@ -7054,19 +7053,19 @@ static void bridge_host_set_selection(struct wlr_data_source *src) {
|
|||
|
||||
if (bridge.backend == BRIDGE_BACKEND_DATA_CONTROL && bridge.dc_device) {
|
||||
if (bridge.dc_source) {
|
||||
zwlr_data_control_source_v1_destroy(bridge.dc_source);
|
||||
ext_data_control_source_v1_destroy(bridge.dc_source);
|
||||
bridge.dc_source = NULL;
|
||||
}
|
||||
struct zwlr_data_control_source_v1 *s =
|
||||
zwlr_data_control_manager_v1_create_data_source(bridge.dc_manager);
|
||||
struct ext_data_control_source_v1 *s =
|
||||
ext_data_control_manager_v1_create_data_source(bridge.dc_manager);
|
||||
char **p;
|
||||
wl_array_for_each(p, &src->mime_types)
|
||||
zwlr_data_control_source_v1_offer(s, *p);
|
||||
zwlr_data_control_source_v1_add_listener(s, &bridge_dc_source_listener, NULL);
|
||||
ext_data_control_source_v1_offer(s, *p);
|
||||
ext_data_control_source_v1_add_listener(s, &bridge_dc_source_listener, NULL);
|
||||
bridge.dc_source = s;
|
||||
bridge.dc_pending_echo = true;
|
||||
bridge.dd_pending_echo = true;
|
||||
zwlr_data_control_device_v1_set_selection(bridge.dc_device, s);
|
||||
ext_data_control_device_v1_set_selection(bridge.dc_device, s);
|
||||
bridge.host_set_pending = false;
|
||||
return;
|
||||
}
|
||||
|
|
@ -7099,7 +7098,7 @@ static void bridge_host_set_selection(struct wlr_data_source *src) {
|
|||
|
||||
static void bridge_host_clear(void) {
|
||||
if (bridge.backend == BRIDGE_BACKEND_DATA_CONTROL && bridge.dc_device)
|
||||
zwlr_data_control_device_v1_set_selection(bridge.dc_device, NULL);
|
||||
ext_data_control_device_v1_set_selection(bridge.dc_device, NULL);
|
||||
/* focus-based backend: nothing reliable to clear without focus/serial */
|
||||
}
|
||||
|
||||
|
|
@ -7109,32 +7108,30 @@ static void bridge_host_set_primary_selection(struct wlr_primary_selection_sourc
|
|||
the mirror's prefetch is stale. */
|
||||
bridge_primary_prefetch_cancel();
|
||||
|
||||
if (bridge.backend != BRIDGE_BACKEND_DATA_CONTROL || !bridge.dc_device
|
||||
|| !bridge.dc_primary_supported)
|
||||
return; /* wl_data_device / v1 data-control have no primary channel */
|
||||
if (bridge.backend != BRIDGE_BACKEND_DATA_CONTROL || !bridge.dc_device)
|
||||
return; /* wl_data_device backend has no primary channel */
|
||||
if (bridge.dc_primary_source) {
|
||||
zwlr_data_control_source_v1_destroy(bridge.dc_primary_source);
|
||||
ext_data_control_source_v1_destroy(bridge.dc_primary_source);
|
||||
bridge.dc_primary_source = NULL;
|
||||
}
|
||||
if (!src)
|
||||
return;
|
||||
struct zwlr_data_control_source_v1 *s =
|
||||
zwlr_data_control_manager_v1_create_data_source(bridge.dc_manager);
|
||||
struct ext_data_control_source_v1 *s =
|
||||
ext_data_control_manager_v1_create_data_source(bridge.dc_manager);
|
||||
char **p;
|
||||
wl_array_for_each(p, &src->mime_types)
|
||||
zwlr_data_control_source_v1_offer(s, *p);
|
||||
zwlr_data_control_source_v1_add_listener(s,
|
||||
ext_data_control_source_v1_offer(s, *p);
|
||||
ext_data_control_source_v1_add_listener(s,
|
||||
&bridge_dc_primary_source_listener, NULL);
|
||||
bridge.dc_primary_source = s;
|
||||
bridge.dc_primary_pending_echo = true;
|
||||
zwlr_data_control_device_v1_set_primary_selection(bridge.dc_device, s);
|
||||
ext_data_control_device_v1_set_primary_selection(bridge.dc_device, s);
|
||||
kick_log(KICK_LOG_INFO, "clipboard bridge: primary selection -> host\n");
|
||||
}
|
||||
|
||||
static void bridge_host_primary_clear(void) {
|
||||
if (bridge.backend == BRIDGE_BACKEND_DATA_CONTROL && bridge.dc_device
|
||||
&& bridge.dc_primary_supported)
|
||||
zwlr_data_control_device_v1_set_primary_selection(bridge.dc_device, NULL);
|
||||
if (bridge.backend == BRIDGE_BACKEND_DATA_CONTROL && bridge.dc_device)
|
||||
ext_data_control_device_v1_set_primary_selection(bridge.dc_device, NULL);
|
||||
}
|
||||
|
||||
static void bridge_seat_set_primary_selection(struct wl_listener *l, void *data) {
|
||||
|
|
@ -7206,18 +7203,18 @@ static void bridge_init(void) {
|
|||
wl_data_device_release(bridge.dd);
|
||||
bridge.dd = NULL;
|
||||
if (bridge.dc_device)
|
||||
zwlr_data_control_device_v1_destroy(bridge.dc_device);
|
||||
ext_data_control_device_v1_destroy(bridge.dc_device);
|
||||
bridge.dc_device = NULL;
|
||||
if (bridge.dc_primary_source) {
|
||||
zwlr_data_control_source_v1_destroy(bridge.dc_primary_source);
|
||||
ext_data_control_source_v1_destroy(bridge.dc_primary_source);
|
||||
bridge.dc_primary_source = NULL;
|
||||
}
|
||||
if (bridge.dc_primary_offer) {
|
||||
zwlr_data_control_offer_v1_destroy(bridge.dc_primary_offer);
|
||||
ext_data_control_offer_v1_destroy(bridge.dc_primary_offer);
|
||||
bridge.dc_primary_offer = NULL;
|
||||
}
|
||||
if (bridge.dc_primary_echo_offer) {
|
||||
zwlr_data_control_offer_v1_destroy(bridge.dc_primary_echo_offer);
|
||||
ext_data_control_offer_v1_destroy(bridge.dc_primary_echo_offer);
|
||||
bridge.dc_primary_echo_offer = NULL;
|
||||
}
|
||||
bridge.dc_primary_pending_echo = false;
|
||||
|
|
@ -7232,9 +7229,9 @@ static void bridge_init(void) {
|
|||
wl_data_device_add_listener(bridge.dd, &bridge_dd_listener, NULL);
|
||||
}
|
||||
if (bridge.dc_manager) {
|
||||
bridge.dc_device = zwlr_data_control_manager_v1_get_data_device(
|
||||
bridge.dc_device = ext_data_control_manager_v1_get_data_device(
|
||||
bridge.dc_manager, host.seat);
|
||||
zwlr_data_control_device_v1_add_listener(bridge.dc_device,
|
||||
ext_data_control_device_v1_add_listener(bridge.dc_device,
|
||||
&bridge_dc_listener, NULL);
|
||||
bridge.backend = BRIDGE_BACKEND_DATA_CONTROL;
|
||||
kick_log(KICK_LOG_INFO, "clipboard bridge: host data-control backend "
|
||||
|
|
@ -7259,11 +7256,11 @@ static void bridge_cleanup(void) {
|
|||
bridge.inner_primary_src = NULL;
|
||||
}
|
||||
if (bridge.dc_source) {
|
||||
zwlr_data_control_source_v1_destroy(bridge.dc_source);
|
||||
ext_data_control_source_v1_destroy(bridge.dc_source);
|
||||
bridge.dc_source = NULL;
|
||||
}
|
||||
if (bridge.dc_primary_source) {
|
||||
zwlr_data_control_source_v1_destroy(bridge.dc_primary_source);
|
||||
ext_data_control_source_v1_destroy(bridge.dc_primary_source);
|
||||
bridge.dc_primary_source = NULL;
|
||||
}
|
||||
if (bridge.dd_source) {
|
||||
|
|
@ -7271,15 +7268,15 @@ static void bridge_cleanup(void) {
|
|||
bridge.dd_source = NULL;
|
||||
}
|
||||
if (bridge.dc_offer) {
|
||||
zwlr_data_control_offer_v1_destroy(bridge.dc_offer);
|
||||
ext_data_control_offer_v1_destroy(bridge.dc_offer);
|
||||
bridge.dc_offer = NULL;
|
||||
}
|
||||
if (bridge.dc_primary_offer) {
|
||||
zwlr_data_control_offer_v1_destroy(bridge.dc_primary_offer);
|
||||
ext_data_control_offer_v1_destroy(bridge.dc_primary_offer);
|
||||
bridge.dc_primary_offer = NULL;
|
||||
}
|
||||
if (bridge.dc_primary_echo_offer) {
|
||||
zwlr_data_control_offer_v1_destroy(bridge.dc_primary_echo_offer);
|
||||
ext_data_control_offer_v1_destroy(bridge.dc_primary_echo_offer);
|
||||
bridge.dc_primary_echo_offer = NULL;
|
||||
}
|
||||
if (bridge.dd_offer) {
|
||||
|
|
@ -7287,7 +7284,7 @@ static void bridge_cleanup(void) {
|
|||
bridge.dd_offer = NULL;
|
||||
}
|
||||
if (bridge.dc_device) {
|
||||
zwlr_data_control_device_v1_destroy(bridge.dc_device);
|
||||
ext_data_control_device_v1_destroy(bridge.dc_device);
|
||||
bridge.dc_device = NULL;
|
||||
}
|
||||
bridge_mimes_reset();
|
||||
|
|
@ -7386,14 +7383,10 @@ static void host_registry_global(void *data, struct wl_registry *registry,
|
|||
bridge.ddm = wl_registry_bind(registry, name,
|
||||
&wl_data_device_manager_interface, version < 3 ? version : 3);
|
||||
} else if (strcmp(interface,
|
||||
zwlr_data_control_manager_v1_interface.name) == 0) {
|
||||
ext_data_control_manager_v1_interface.name) == 0) {
|
||||
bridge.dc_manager = wl_registry_bind(registry, name,
|
||||
&zwlr_data_control_manager_v1_interface, version < 2 ? version : 2);
|
||||
bridge.dc_primary_supported =
|
||||
wl_proxy_get_version((struct wl_proxy *)bridge.dc_manager) >= 2;
|
||||
if (!bridge.dc_primary_supported)
|
||||
kick_log(KICK_LOG_INFO, "clipboard bridge: host wlr-data-control "
|
||||
"v1 only; primary selection not bridged\n");
|
||||
&ext_data_control_manager_v1_interface, 1);
|
||||
kick_log(KICK_LOG_DEBUG, "clipboard bridge: host ext-data-control\n");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -11380,7 +11373,7 @@ int main(int argc, char *argv[]) {
|
|||
wlr_shm_create_with_renderer(server.wl_display, 1, server.renderer);
|
||||
wlr_linux_dmabuf_v1_create_with_renderer(server.wl_display, 5, server.renderer);
|
||||
wlr_data_device_manager_create(server.wl_display);
|
||||
wlr_data_control_manager_v1_create(server.wl_display);
|
||||
wlr_ext_data_control_manager_v1_create(server.wl_display, 1);
|
||||
|
||||
/* Direct scanout policy:
|
||||
- standalone (DRM/VT console): ENABLED. Scanout presents a fullscreen
|
||||
|
|
|
|||
12
meson.build
12
meson.build
|
|
@ -96,14 +96,14 @@ protocols = {
|
|||
wl_protocols_dir / 'unstable/xdg-decoration/xdg-decoration-unstable-v1.xml',
|
||||
'xdg-decoration',
|
||||
],
|
||||
'wlr-data-control-unstable-v1': [
|
||||
'wlr-protocols/wlr-data-control-unstable-v1.xml',
|
||||
'wlr-data-control',
|
||||
'ext-data-control': [
|
||||
wl_protocols_dir / 'staging/ext-data-control/ext-data-control-v1.xml',
|
||||
'ext-data-control-v1',
|
||||
],
|
||||
# wlr-layer-shell is what waybar and other panels use to make an
|
||||
# always-visible bar. Bundled locally (like wlr-data-control) rather than
|
||||
# referenced from the wlroots tree, so it works with either a system
|
||||
# wlroots or the pinned subproject.
|
||||
# always-visible bar. Bundled locally (like the foreign-toplevel and
|
||||
# screencopy XMLs) rather than referenced from the wlroots tree, so it
|
||||
# works with either a system wlroots or the pinned subproject.
|
||||
'wlr-layer-shell-unstable-v1': [
|
||||
'wlr-protocols/wlr-layer-shell-unstable-v1.xml',
|
||||
'wlr-layer-shell-unstable-v1',
|
||||
|
|
|
|||
|
|
@ -1822,7 +1822,7 @@ def test_standalone_headless_gestures_roundtrip(tmp_path):
|
|||
def test_nested_primary_selection_bridged(tmp_path):
|
||||
"""Nested mode: a tab's primary selection is fanned out to a second tab
|
||||
exactly as standalone, and the host-side bridge publishes it over
|
||||
wlr-data-control (when the host implements v2) without echo-looping."""
|
||||
ext-data-control (when the host implements it) without echo-looping."""
|
||||
if not os.environ.get("WAYLAND_DISPLAY"):
|
||||
pytest.skip("needs a live host Wayland session (WAYLAND_DISPLAY)")
|
||||
out = tmp_path / "primary.out"
|
||||
|
|
@ -1867,9 +1867,9 @@ def test_nested_host_primary_to_kick(tmp_path):
|
|||
payload* because a live host may already hold its own primary selection at
|
||||
boot, which kick faithfully mirrors first.
|
||||
|
||||
Skipped on hosts without wlr-data-control v2 (e.g. GNOME/mutter): the
|
||||
Skipped on hosts without ext-data-control (e.g. GNOME/mutter): the
|
||||
host->nested primary mirror only exists on that backend, so this test
|
||||
needs a data-control host -- run it on a compositor like labwc/sway."""
|
||||
needs an ext-data-control host -- run it on a compositor like labwc/sway."""
|
||||
if not os.environ.get("WAYLAND_DISPLAY"):
|
||||
pytest.skip("needs a live host Wayland session (WAYLAND_DISPLAY)")
|
||||
token = f"kick-h2n-{os.getpid()}-{time.monotonic_ns()}"
|
||||
|
|
@ -1881,12 +1881,12 @@ def test_nested_host_primary_to_kick(tmp_path):
|
|||
s.wait_any_active()
|
||||
s.wait_active_frame()
|
||||
assert s.proc.poll() is None
|
||||
# The host->nested primary bridge only exists over wlr-data-control
|
||||
# v2 (a focus-based wl_data_device host has no primary channel at
|
||||
# The host->nested primary bridge only exists over ext-data-control
|
||||
# (a focus-based wl_data_device host has no primary channel at
|
||||
# all), which is decided at bridge_init before any client maps.
|
||||
s.wait_for(r"clipboard bridge:")
|
||||
if "clipboard bridge: host data-control backend" not in s.read_log():
|
||||
pytest.skip("host compositor lacks wlr-data-control v2; "
|
||||
pytest.skip("host compositor lacks ext-data-control; "
|
||||
"host->nested primary selection is not bridged here")
|
||||
# Primary offers only reach the *focused* tab (wlroots delivers
|
||||
# device selection events to the focused client), so crown the
|
||||
|
|
|
|||
|
|
@ -1,278 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<protocol name="wlr_data_control_unstable_v1">
|
||||
<copyright>
|
||||
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.
|
||||
</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="2">
|
||||
<description summary="manager to control data devices">
|
||||
This interface is a manager that allows creating per-seat data device
|
||||
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_device">
|
||||
<description summary="get a data device for a seat">
|
||||
Create a data device that can be used to manage a seat's selection.
|
||||
</description>
|
||||
<arg name="id" type="new_id" interface="zwlr_data_control_device_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_device_v1" version="2">
|
||||
<description summary="manage a data device for a seat">
|
||||
This interface allows a client to manage a seat's selection.
|
||||
|
||||
When the seat is destroyed, this object becomes inert.
|
||||
</description>
|
||||
|
||||
<request name="set_selection">
|
||||
<description summary="copy data to the selection">
|
||||
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.
|
||||
</description>
|
||||
<arg name="source" type="object" interface="zwlr_data_control_source_v1"
|
||||
allow-null="true"/>
|
||||
</request>
|
||||
|
||||
<request name="destroy" type="destructor">
|
||||
<description summary="destroy this data device">
|
||||
Destroys the data device 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 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.
|
||||
</description>
|
||||
<arg name="id" type="new_id" interface="zwlr_data_control_offer_v1"/>
|
||||
</event>
|
||||
|
||||
<event name="selection">
|
||||
<description summary="advertise new selection">
|
||||
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.
|
||||
</description>
|
||||
<arg name="id" type="object" interface="zwlr_data_control_offer_v1"
|
||||
allow-null="true"/>
|
||||
</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>
|
||||
|
||||
<!-- Version 2 additions -->
|
||||
|
||||
<event name="primary_selection" since="2">
|
||||
<description summary="advertise new primary selection">
|
||||
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.
|
||||
</description>
|
||||
<arg name="id" type="object" interface="zwlr_data_control_offer_v1"
|
||||
allow-null="true"/>
|
||||
</event>
|
||||
|
||||
<request name="set_primary_selection" since="2">
|
||||
<description summary="copy data to the primary selection">
|
||||
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.
|
||||
</description>
|
||||
<arg name="source" type="object" interface="zwlr_data_control_source_v1"
|
||||
allow-null="true"/>
|
||||
</request>
|
||||
|
||||
<enum name="error" since="2">
|
||||
<entry name="used_source" value="1"
|
||||
summary="source given to set_selection or set_primary_selection was already used before"/>
|
||||
</enum>
|
||||
</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_device.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_device.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>
|
||||
Loading…
Add table
Add a link
Reference in a new issue