server: verify selection offer is still valid when handling clipboard

Should fix #170, fix #183
This commit is contained in:
Shawn Wallace 2025-06-29 01:37:24 -04:00
parent 2e7c318ac2
commit cf1fae1eae
4 changed files with 49 additions and 14 deletions

View file

@ -3,7 +3,8 @@ use hecs::{Entity, World};
use smithay_client_toolkit::{
activation::{ActivationHandler, RequestData, RequestDataExt},
data_device_manager::{
data_device::DataDeviceHandler, data_offer::DataOfferHandler,
data_device::{DataDeviceData, DataDeviceHandler},
data_offer::{DataOfferHandler, SelectionOffer},
data_source::DataSourceHandler,
},
delegate_activation, delegate_data_device,
@ -78,7 +79,7 @@ pub(super) struct MyWorld {
pub new_globals: Vec<Global>,
events: Vec<(Entity, ObjectEvent)>,
queued_events: Vec<mpsc::Receiver<(Entity, ObjectEvent)>>,
pub selection: Option<wayland_client::protocol::wl_data_device::WlDataDevice>,
pub selection_offer: Option<SelectionOffer>,
pub selection_requests: Vec<(
String,
smithay_client_toolkit::data_device_manager::WritePipe,
@ -95,7 +96,7 @@ impl MyWorld {
new_globals: Vec::new(),
events: Vec::new(),
queued_events: Vec::new(),
selection: None,
selection_offer: None,
selection_requests: Vec::new(),
selection_cancelled: false,
pending_activations: Vec::new(),
@ -385,7 +386,8 @@ impl DataDeviceHandler for MyWorld {
_: &wayland_client::QueueHandle<Self>,
data_device: &wayland_client::protocol::wl_data_device::WlDataDevice,
) {
self.selection = Some(data_device.clone());
let data: &DataDeviceData = data_device.data().unwrap();
self.selection_offer = data.selection_offer();
}
fn drop_performed(

View file

@ -1095,16 +1095,18 @@ impl<C: XConnection> ServerState<C> {
}
}
if clipboard.source.is_none() || self.world.selection_cancelled {
if self.world.selection.take().is_some() {
let device = clipboard.device.as_ref().unwrap();
let offer = device.data().selection_offer().unwrap();
let mime_types: Box<[String]> = offer.with_mime_types(|mimes| mimes.into());
let foreign = ForeignSelection {
mime_types,
inner: offer,
};
clipboard.source = Some(CopyPasteData::Foreign(foreign));
if clipboard.source.is_none() {
if let Some(offer) = self.world.selection_offer.take() {
if offer.inner().is_alive() {
let mime_types: Box<[String]> = offer.with_mime_types(|mimes| mimes.into());
let foreign = ForeignSelection {
mime_types,
inner: offer,
};
clipboard.source = Some(CopyPasteData::Foreign(foreign));
} else {
clipboard.source = None;
}
}
self.world.selection_cancelled = false;
}

View file

@ -2427,6 +2427,23 @@ fn output_updated_before_x_connection() {
assert_eq!(data.dims.x, 0);
assert_eq!(data.dims.y, 0);
}
#[test]
fn quick_empty_data_offer() {
let (mut f, comp) = TestFixture::new_with_compositor();
TestObject::<WlKeyboard>::from_request(&comp.seat.obj, wl_seat::Request::GetKeyboard {});
let win = unsafe { Window::new(1) };
let (_surface, _id) = f.create_toplevel(&comp, win);
f.testwl.create_data_offer(vec![testwl::PasteData {
mime_type: "text".to_string(),
data: b"abc".to_vec(),
}]);
f.testwl.empty_data_offer();
f.run();
let selection = f.satellite.new_selection();
assert!(selection.is_none());
}
/// See Pointer::handle_event for an explanation.
#[test]
fn popup_pointer_motion_workaround() {}