From 087ebaa2c7e421cd7f5fcbee8f1b536e09ea2abe Mon Sep 17 00:00:00 2001 From: Victoria Brekenfeld Date: Wed, 28 May 2025 16:16:56 +0200 Subject: [PATCH] xwayland: Delay selection notify until focused --- src/wayland/handlers/selection.rs | 35 ++++++++++++++++++++++++----- src/xwayland.rs | 37 ++++++++++++++++++++++++++++--- 2 files changed, 63 insertions(+), 9 deletions(-) diff --git a/src/wayland/handlers/selection.rs b/src/wayland/handlers/selection.rs index 6d157a5f..a2520b72 100644 --- a/src/wayland/handlers/selection.rs +++ b/src/wayland/handlers/selection.rs @@ -18,19 +18,42 @@ impl SelectionHandler for State { source: Option, _seat: Seat, ) { - if let Some(xwm) = self + let Some(xwm_id) = self .common .xwayland_state - .as_mut() - .and_then(|xstate| xstate.xwm.as_mut()) - { - if let Some(source) = &source { + .as_ref() + .and_then(|xstate| xstate.xwm.as_ref()) + .map(|xwm| xwm.id()) + else { + return; + }; + + let x_has_focus = self.common.has_x_keyboard_focus(xwm_id); + + let xstate = self.common.xwayland_state.as_mut().unwrap(); + let xwm = xstate.xwm.as_mut().unwrap(); + + if let Some(source) = &source { + if x_has_focus { if let Err(err) = xwm.new_selection(target, Some(source.mime_types())) { warn!(?err, "Failed to set Xwayland clipboard selection."); } - } else if let Err(err) = xwm.new_selection(target, None) { + } else { + match target { + SelectionTarget::Clipboard => { + xstate.clipboard_selection_dirty = Some(source.mime_types()) + } + SelectionTarget::Primary => { + xstate.primary_selection_dirty = Some(source.mime_types()) + } + }; + } + } else { + if let Err(err) = xwm.new_selection(target, None) { warn!(?err, "Failed to clear Xwayland selection."); } + xstate.clipboard_selection_dirty = None; + xstate.primary_selection_dirty = None; } } diff --git a/src/xwayland.rs b/src/xwayland.rs index cabf683c..251020b5 100644 --- a/src/xwayland.rs +++ b/src/xwayland.rs @@ -64,6 +64,8 @@ pub struct XWaylandState { pub pressed_keys: Vec, pub pressed_buttons: Vec, pub last_modifier_state: Option, + pub clipboard_selection_dirty: Option>, + pub primary_selection_dirty: Option>, } impl State { @@ -108,6 +110,8 @@ impl State { pressed_keys: Vec::new(), pressed_buttons: Vec::new(), last_modifier_state: None, + clipboard_selection_dirty: None, + primary_selection_dirty: None, }); let wm = match X11Wm::start_wm( @@ -244,7 +248,7 @@ impl XWaylandState { } impl Common { - fn has_x_keyboard_focus(&self, xwmid: XwmId) -> bool { + pub fn has_x_keyboard_focus(&self, xwmid: XwmId) -> bool { let keyboard = self .shell .read() @@ -290,10 +294,37 @@ impl Common { if target .as_ref() .is_some_and(|target| matches!(target, KeyboardFocusTarget::LockSurface(_))) - || (!self.has_x_keyboard_focus(xwm_id) - && target.as_ref().is_some_and(|target| target.is_xwm(xwm_id))) { self.xwayland_reset_eavesdropping(serial); + return; + } + + if !self.has_x_keyboard_focus(xwm_id) + && target.as_ref().is_some_and(|target| target.is_xwm(xwm_id)) + { + self.xwayland_reset_eavesdropping(serial); + + let xstate = self.xwayland_state.as_mut().unwrap(); + if let Some(mime_types) = xstate.clipboard_selection_dirty.take() { + if let Err(err) = xstate + .xwm + .as_mut() + .unwrap() + .new_selection(SelectionTarget::Clipboard, Some(mime_types)) + { + warn!(?err, "Failed to set Xwayland clipboard selection."); + } + } + if let Some(mime_types) = xstate.primary_selection_dirty.take() { + if let Err(err) = xstate + .xwm + .as_mut() + .unwrap() + .new_selection(SelectionTarget::Primary, Some(mime_types)) + { + warn!(?err, "Failed to set Xwayland clipboard selection."); + } + } } } }