diff --git a/tabbed-wlroots.c b/tabbed-wlroots.c index 512a9a1..0de45b9 100644 --- a/tabbed-wlroots.c +++ b/tabbed-wlroots.c @@ -1430,8 +1430,8 @@ static void bridge_dc_device_selection(void *data, if (offer && offer == bridge.dc_echo_offer) { bridge.dc_echo_offer = NULL; bridge_mimes_reset(); - if (offer) - zwlr_data_control_offer_v1_destroy(offer); + zwlr_data_control_offer_v1_destroy(offer); + bridge.dc_pending_echo = false; return; } bridge.dc_pending_echo = false; @@ -1516,12 +1516,20 @@ static void bridge_dd_data_offer(void *data, struct wl_data_device *dev, static void bridge_dd_selection(void *data, struct wl_data_device *dev, struct wl_data_offer *offer) { (void)data; (void)dev; + if (!offer) { + /* Focus blip: the host selection is momentarily gone (or we lost + keyboard focus). Keep the last-known nested selection and, above + all, leave the pending-echo flags intact so an in-flight dc/dd + set_selection reply is still recognized as our own echo. */ + bridge_mimes_reset(); + return; + } bridge_prefetch_cancel(); - if (offer && offer == bridge.dd_echo_offer) { + if (offer == bridge.dd_echo_offer) { bridge.dd_echo_offer = NULL; bridge_mimes_reset(); - if (offer) - wl_data_offer_destroy(offer); + wl_data_offer_destroy(offer); + bridge.dd_pending_echo = false; return; } bridge.dc_pending_echo = false; @@ -1529,12 +1537,6 @@ static void bridge_dd_selection(void *data, struct wl_data_device *dev, if (bridge.dd_offer) wl_data_offer_destroy(bridge.dd_offer); bridge.dd_offer = offer; - if (!offer) { - /* selection cleared, or we lost keyboard focus: keep the last-known - nested selection rather than emptying it on every focus change */ - bridge_mimes_reset(); - return; - } bridge_prefetch_start(); bridge_mirror_to_nested(); } @@ -1889,11 +1891,18 @@ static void host_kbd_enter(void *data, struct wl_keyboard *kbd, uint32_t serial, struct wl_surface *surface, struct wl_array *keys) { (void)data; (void)kbd; (void)surface; (void)keys; - /* Remember the enter serial for host clipboard set_selection, and drop any - stale echo guard from a set_selection made while unfocused. */ + /* Remember the enter serial for host clipboard set_selection. Only the + focus-based wl_data_device backend can hold a stale echo guard (a + set_selection sent with a no-longer-valid serial gets no reply): drop it + on refocus there. The data-control backend echoes independently of + focus, so its in-flight guard must be preserved here, or the host's echo + of our own set_selection is mistaken for a real selection and mirrored + back into an endless set_selection/receive loop. */ bridge.kbd_serial = serial; - bridge.dc_pending_echo = false; - bridge.dd_pending_echo = false; + if (bridge.backend == BRIDGE_BACKEND_WL_DATA_DEVICE) { + 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;