diff --git a/anvil/src/shell/x11.rs b/anvil/src/shell/x11.rs index 1740749a4f..9d5ce0c3c5 100644 --- a/anvil/src/shell/x11.rs +++ b/anvil/src/shell/x11.rs @@ -1,18 +1,28 @@ -use std::cell::RefCell; +use std::{cell::RefCell, os::unix::io::OwnedFd}; use smithay::{ desktop::space::SpaceElement, input::pointer::Focus, utils::{Logical, Rectangle, SERIAL_COUNTER}, - wayland::compositor::with_states, + wayland::{ + compositor::with_states, + data_device::{ + clear_data_device_selection, current_data_device_selection_userdata, + request_data_device_client_selection, set_data_device_selection, + }, + primary_selection::{ + clear_primary_selection, current_primary_selection_userdata, request_primary_client_selection, + set_primary_selection, + }, + }, xwayland::{ - xwm::{Reorder, ResizeEdge as X11ResizeEdge, XwmId}, + xwm::{Reorder, ResizeEdge as X11ResizeEdge, SelectionType, XwmId}, X11Surface, X11Wm, XwmHandler, }, }; -use tracing::trace; +use tracing::{error, trace}; -use crate::{state::Backend, AnvilState, CalloopData}; +use crate::{focus::FocusTarget, state::Backend, AnvilState, CalloopData}; use super::{ place_new_window, FullscreenSurface, MoveSurfaceGrab, ResizeData, ResizeState, ResizeSurfaceGrab, @@ -232,6 +242,64 @@ impl XwmHandler for CalloopData { fn move_request(&mut self, _xwm: XwmId, window: X11Surface, _button: u32) { self.state.move_request_x11(&window) } + + fn allow_selection_access(&mut self, xwm: XwmId, _selection: SelectionType) -> bool { + if let Some(keyboard) = self.state.seat.get_keyboard() { + // check that an X11 window is focused + if let Some(FocusTarget::Window(WindowElement::X11(surface))) = keyboard.current_focus() { + if surface.xwm_id().unwrap() == xwm { + return true; + } + } + } + false + } + + fn send_selection(&mut self, _xwm: XwmId, selection: SelectionType, mime_type: String, fd: OwnedFd) { + match selection { + SelectionType::Clipboard => { + if let Err(err) = request_data_device_client_selection(&self.state.seat, mime_type, fd) { + error!(?err, "Failed to request current wayland clipboard for Xwayland",); + } + } + SelectionType::Primary => { + if let Err(err) = request_primary_client_selection(&self.state.seat, mime_type, fd) { + error!( + ?err, + "Failed to request current wayland primary selection for Xwayland", + ); + } + } + } + } + + fn new_selection(&mut self, _xwm: XwmId, selection: SelectionType, mime_types: Vec) { + trace!(?selection, ?mime_types, "Got Selection from X11",); + // TODO check, that focused windows is X11 window before doing this + match selection { + SelectionType::Clipboard => { + set_data_device_selection(&self.state.display_handle, &self.state.seat, mime_types, ()) + } + SelectionType::Primary => { + set_primary_selection(&self.state.display_handle, &self.state.seat, mime_types, ()) + } + } + } + + fn cleared_selection(&mut self, _xwm: XwmId, selection: SelectionType) { + match selection { + SelectionType::Clipboard => { + if current_data_device_selection_userdata(&self.state.seat).is_some() { + clear_data_device_selection(&self.state.display_handle, &self.state.seat) + } + } + SelectionType::Primary => { + if current_primary_selection_userdata(&self.state.seat).is_some() { + clear_primary_selection(&self.state.display_handle, &self.state.seat) + } + } + } + } } impl AnvilState { diff --git a/anvil/src/state.rs b/anvil/src/state.rs index c51433665d..8f1041e9ca 100644 --- a/anvil/src/state.rs +++ b/anvil/src/state.rs @@ -77,8 +77,13 @@ use crate::cursor::Cursor; use crate::{focus::FocusTarget, shell::WindowElement}; #[cfg(feature = "xwayland")] use smithay::{ + reexports::wayland_protocols::wp::primary_selection::zv1::server::zwp_primary_selection_source_v1::ZwpPrimarySelectionSourceV1, utils::Size, - xwayland::{X11Wm, XWayland, XWaylandEvent}, + wayland::{ + data_device::with_source_metadata as with_data_device_source_metadata, + primary_selection::with_source_metadata as with_primary_source_metadata, + }, + xwayland::{xwm::SelectionType, X11Wm, XWayland, XWaylandEvent}, }; pub struct CalloopData { @@ -156,8 +161,30 @@ impl DataDeviceHandler for AnvilState { fn data_device_state(&self) -> &DataDeviceState { &self.data_device_state } - fn send_selection(&mut self, _mime_type: String, _fd: OwnedFd, _seat: Seat) { - unreachable!("Anvil doesn't do server-side selections"); + + #[cfg(feature = "xwayland")] + fn new_selection(&mut self, source: Option, _seat: Seat) { + if let Some(xwm) = self.xwm.as_mut() { + if let Some(source) = source { + if let Ok(Err(err)) = with_data_device_source_metadata(&source, |metadata| { + xwm.new_selection(SelectionType::Clipboard, Some(metadata.mime_types.clone())) + }) { + warn!(?err, "Failed to set Xwayland clipboard selection"); + } + } else if let Err(err) = xwm.new_selection(SelectionType::Clipboard, None) { + warn!(?err, "Failed to clear Xwayland clipboard selection"); + } + } + } + + #[cfg(feature = "xwayland")] + fn send_selection(&mut self, mime_type: String, fd: OwnedFd, _seat: Seat, _user_data: &()) { + if let Some(xwm) = self.xwm.as_mut() { + if let Err(err) = xwm.send_selection(SelectionType::Clipboard, mime_type, fd, self.handle.clone()) + { + warn!(?err, "Failed to send clipboard (X11 -> Wayland)"); + } + } } } impl ClientDndGrabHandler for AnvilState { @@ -182,6 +209,30 @@ impl PrimarySelectionHandler for AnvilState { fn primary_selection_state(&self) -> &PrimarySelectionState { &self.primary_selection_state } + + #[cfg(feature = "xwayland")] + fn new_selection(&mut self, source: Option, _seat: Seat) { + if let Some(xwm) = self.xwm.as_mut() { + if let Some(source) = source { + if let Ok(Err(err)) = with_primary_source_metadata(&source, |metadata| { + xwm.new_selection(SelectionType::Primary, Some(metadata.mime_types.clone())) + }) { + warn!(?err, "Failed to set Xwayland primary selection"); + } + } else if let Err(err) = xwm.new_selection(SelectionType::Primary, None) { + warn!(?err, "Failed to clear Xwayland primary selection"); + } + } + } + + #[cfg(feature = "xwayland")] + fn send_selection(&mut self, mime_type: String, fd: OwnedFd, _seat: Seat, _user_data: &()) { + if let Some(xwm) = self.xwm.as_mut() { + if let Err(err) = xwm.send_selection(SelectionType::Primary, mime_type, fd, self.handle.clone()) { + warn!(?err, "Failed to send primary (X11 -> Wayland)"); + } + } + } } delegate_primary_selection!(@ AnvilState);