Fix wlr-data-control clipboard: handle primary_selection and echo ordering
labwc sends the primary_selection event (opcode 3) on bind; leaving the listener unset made libwayland abort. Acknowledge and destroy the offer (primary selection is not bridged). Echo handling used one shared pending_echo flag: when the host selection is set via data-control, the compositor echoes the seat change over both the wl_data_device and data-control channels, so the first echo consumed the shared flag and the second was misread as a real host selection, clobbering the nested source via mirror_to_nested. Track the pending echo per backend and clear both on any real selection. Verified: clipboard tests pass on labwc (data-control) and GNOME/Mutter (wl_data_device); full 7-test suite green on both hosts.
This commit is contained in:
parent
fec8266154
commit
ad46508962
1 changed files with 32 additions and 11 deletions
|
|
@ -224,7 +224,8 @@ struct clipboard_bridge {
|
|||
/* MIME list currently being collected from a host offer */
|
||||
char **mimes;
|
||||
size_t mimes_len, mimes_cap;
|
||||
bool pending_echo; /* set before host set_selection to catch its echo */
|
||||
bool dc_pending_echo; /* our own set via data-control: echo not yet consumed */
|
||||
bool dd_pending_echo; /* our own set via wl_data_device: echo not yet consumed */
|
||||
bool host_set_pending; /* nested selection not yet on the host; retry on focus */
|
||||
|
||||
/* Eager text prefetch: when a real host selection is mirrored, its primary
|
||||
|
|
@ -1371,7 +1372,8 @@ static void bridge_dc_source_cancelled(void *data,
|
|||
(void)data;
|
||||
zwlr_data_control_source_v1_destroy(src);
|
||||
bridge.dc_source = NULL;
|
||||
bridge.pending_echo = false;
|
||||
bridge.dc_pending_echo = false;
|
||||
bridge.dd_pending_echo = false;
|
||||
}
|
||||
|
||||
static const struct zwlr_data_control_source_v1_listener bridge_dc_source_listener = {
|
||||
|
|
@ -1412,8 +1414,8 @@ static void bridge_dc_device_data_offer(void *data,
|
|||
struct zwlr_data_control_device_v1 *dev,
|
||||
struct zwlr_data_control_offer_v1 *offer) {
|
||||
(void)data; (void)dev;
|
||||
if (bridge.pending_echo) {
|
||||
bridge.pending_echo = false;
|
||||
if (bridge.dc_pending_echo) {
|
||||
bridge.dc_pending_echo = false;
|
||||
bridge.dc_echo_offer = offer;
|
||||
}
|
||||
bridge_mimes_reset();
|
||||
|
|
@ -1432,6 +1434,8 @@ static void bridge_dc_device_selection(void *data,
|
|||
zwlr_data_control_offer_v1_destroy(offer);
|
||||
return;
|
||||
}
|
||||
bridge.dc_pending_echo = false;
|
||||
bridge.dd_pending_echo = false;
|
||||
if (bridge.dc_offer)
|
||||
zwlr_data_control_offer_v1_destroy(bridge.dc_offer);
|
||||
bridge.dc_offer = offer;
|
||||
|
|
@ -1444,11 +1448,22 @@ static void bridge_dc_device_selection(void *data,
|
|||
bridge_mirror_to_nested();
|
||||
}
|
||||
|
||||
/* Primary selection is intentionally not bridged; the offer must still be
|
||||
acknowledged (NULL handler would make libwayland abort on the event) and
|
||||
destroyed so it does not leak. */
|
||||
static void bridge_dc_device_primary_selection(void *data,
|
||||
struct zwlr_data_control_device_v1 *dev,
|
||||
struct zwlr_data_control_offer_v1 *offer) {
|
||||
(void)data; (void)dev;
|
||||
if (offer)
|
||||
zwlr_data_control_offer_v1_destroy(offer);
|
||||
}
|
||||
|
||||
static const struct zwlr_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,
|
||||
.finished = bridge_dc_device_finished,
|
||||
/* primary_selection left unset: not bridged */
|
||||
};
|
||||
|
||||
/* ---------------- host backend: wl_data_device (focus-based) ---------------- */
|
||||
|
|
@ -1466,7 +1481,8 @@ static void bridge_dd_source_cancelled(void *data, struct wl_data_source *src) {
|
|||
(void)data;
|
||||
wl_data_source_destroy(src);
|
||||
bridge.dd_source = NULL;
|
||||
bridge.pending_echo = false;
|
||||
bridge.dc_pending_echo = false;
|
||||
bridge.dd_pending_echo = false;
|
||||
bridge.host_set_pending = true;
|
||||
}
|
||||
|
||||
|
|
@ -1489,8 +1505,8 @@ static const struct wl_data_offer_listener bridge_dd_offer_listener = {
|
|||
static void bridge_dd_data_offer(void *data, struct wl_data_device *dev,
|
||||
struct wl_data_offer *offer) {
|
||||
(void)data; (void)dev;
|
||||
if (bridge.pending_echo) {
|
||||
bridge.pending_echo = false;
|
||||
if (bridge.dd_pending_echo) {
|
||||
bridge.dd_pending_echo = false;
|
||||
bridge.dd_echo_offer = offer;
|
||||
}
|
||||
bridge_mimes_reset();
|
||||
|
|
@ -1508,6 +1524,8 @@ static void bridge_dd_selection(void *data, struct wl_data_device *dev,
|
|||
wl_data_offer_destroy(offer);
|
||||
return;
|
||||
}
|
||||
bridge.dc_pending_echo = false;
|
||||
bridge.dd_pending_echo = false;
|
||||
if (bridge.dd_offer)
|
||||
wl_data_offer_destroy(bridge.dd_offer);
|
||||
bridge.dd_offer = offer;
|
||||
|
|
@ -1551,7 +1569,8 @@ static void bridge_host_set_selection(struct wlr_data_source *src) {
|
|||
zwlr_data_control_source_v1_offer(s, *p);
|
||||
zwlr_data_control_source_v1_add_listener(s, &bridge_dc_source_listener, NULL);
|
||||
bridge.dc_source = s;
|
||||
bridge.pending_echo = true;
|
||||
bridge.dc_pending_echo = true;
|
||||
bridge.dd_pending_echo = true;
|
||||
zwlr_data_control_device_v1_set_selection(bridge.dc_device, s);
|
||||
bridge.host_set_pending = false;
|
||||
return;
|
||||
|
|
@ -1575,7 +1594,8 @@ static void bridge_host_set_selection(struct wlr_data_source *src) {
|
|||
wl_data_source_offer(s, *p);
|
||||
wl_data_source_add_listener(s, &bridge_dd_source_listener, NULL);
|
||||
bridge.dd_source = s;
|
||||
bridge.pending_echo = true;
|
||||
bridge.dc_pending_echo = true;
|
||||
bridge.dd_pending_echo = true;
|
||||
wl_data_device_set_selection(bridge.dd, s, bridge.kbd_serial);
|
||||
bridge.host_set_pending = false;
|
||||
return;
|
||||
|
|
@ -1872,7 +1892,8 @@ static void host_kbd_enter(void *data, struct wl_keyboard *kbd, uint32_t serial,
|
|||
/* Remember the enter serial for host clipboard set_selection, and drop any
|
||||
stale echo guard from a set_selection made while unfocused. */
|
||||
bridge.kbd_serial = serial;
|
||||
bridge.pending_echo = false;
|
||||
bridge.dc_pending_echo = false;
|
||||
bridge.dd_pending_echo = false;
|
||||
if (bridge.host_set_pending && server.seat->selection_source &&
|
||||
server.seat->selection_source->impl != &bridge_inner_source_impl) {
|
||||
bridge.host_set_pending = false;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue