diff --git a/src/server/selection.rs b/src/server/selection.rs index ef2b30f..901b472 100644 --- a/src/server/selection.rs +++ b/src/server/selection.rs @@ -240,9 +240,10 @@ impl SelectionType for Clipboard { fn receive_offer(offer: &Self::Offer, mime_type: String) -> std::io::Result { offer.receive(mime_type).map_err(|e| { + use smithay_client_toolkit::data_device_manager::data_offer::DataOfferError; match e { - smithay_client_toolkit::data_device_manager::data_offer::DataOfferError::InvalidReceive => std::io::Error::from(std::io::ErrorKind::Other), - smithay_client_toolkit::data_device_manager::data_offer::DataOfferError::Io(e) => e + DataOfferError::InvalidReceive => std::io::Error::from(std::io::ErrorKind::Other), + DataOfferError::Io(e) => e, } }) } diff --git a/src/xstate/selection.rs b/src/xstate/selection.rs index 8736109..4e3258a 100644 --- a/src/xstate/selection.rs +++ b/src/xstate/selection.rs @@ -250,10 +250,8 @@ trait SelectionDataImpl { atoms: &super::Atoms, request: &x::SelectionRequestEvent, max_req_bytes: usize, - success: &dyn Fn(), - refuse: &dyn Fn(), server_state: &mut RealServerState, - ); + ) -> bool; fn atom(&self) -> x::Atom; } @@ -418,10 +416,8 @@ impl SelectionDataImpl for SelectionData { atoms: &super::Atoms, request: &x::SelectionRequestEvent, max_req_bytes: usize, - success: &dyn Fn(), - refuse: &dyn Fn(), server_state: &mut RealServerState, - ) { + ) -> bool { let Some(CurrentSelection::Wayland(WaylandSelection { mimes, inner, @@ -429,88 +425,84 @@ impl SelectionDataImpl for SelectionData { })) = &mut self.current_selection else { warn!("Got selection request, but we don't seem to be the selection owner"); - refuse(); - return; + return false; }; - match request.target() { - x if x == atoms.targets => { - let atoms: Box<[x::Atom]> = mimes.iter().map(|t| t.atom).collect(); + let req_target = request.target(); + if req_target == atoms.targets { + let atoms: Box<[x::Atom]> = mimes.iter().map(|t| t.atom).collect(); - connection - .send_and_check_request(&x::ChangeProperty { - mode: x::PropMode::Replace, - window: request.requestor(), - property: request.property(), - r#type: x::ATOM_ATOM, - data: &atoms, - }) - .unwrap(); + connection + .send_and_check_request(&x::ChangeProperty { + mode: x::PropMode::Replace, + window: request.requestor(), + property: request.property(), + r#type: x::ATOM_ATOM, + data: &atoms, + }) + .unwrap(); - success(); - } - other => { - let Some(target) = mimes.iter().find(|t| t.atom == other) else { - if log::log_enabled!(log::Level::Debug) { - let name = get_atom_name(connection, other); - debug!("refusing selection request because given atom could not be found ({name})"); - } - refuse(); - return; - }; - - let mime_name = target - .source - .as_ref() - .cloned() - .unwrap_or_else(|| target.name.clone()); - let data = inner.receive(mime_name, server_state); - if data.len() > max_req_bytes { - if let Err(e) = connection.send_and_check_request(&x::ChangeWindowAttributes { - window: request.requestor(), - value_list: &[x::Cw::EventMask(x::EventMask::PROPERTY_CHANGE)], - }) { - warn!("Failed to set up property change notifications: {e:?}"); - refuse(); - return; - } - if let Err(e) = connection.send_and_check_request(&x::ChangeProperty { - mode: x::PropMode::Replace, - window: request.requestor(), - property: request.property(), - r#type: atoms.incr, - data: &[data.len() as u32], - }) { - warn!("Failed to set incr property for large transfer: {e:?}"); - refuse(); - return; - } + true + } else { + let Some(target) = mimes.iter().find(|t| t.atom == req_target) else { + if log::log_enabled!(log::Level::Debug) { + let name = get_atom_name(connection, req_target); debug!( - "beginning incr for {}", - get_atom_name(connection, target.atom) + "refusing selection request because given atom could not be found ({name})" ); - *incr_data = Some(WaylandIncrInfo { - range: 0..data.len(), - data, - target_window: request.requestor(), - property: request.property(), - target_type: target.atom, - max_req_bytes, - }); - success() - } else { - match connection.send_and_check_request(&x::ChangeProperty { - mode: x::PropMode::Replace, - window: request.requestor(), - property: request.property(), - r#type: target.atom, - data: &data, - }) { - Ok(_) => success(), - Err(e) => { - warn!("Failed setting selection property: {e:?}"); - refuse(); - } + } + return false; + }; + + let mime_name = target + .source + .as_ref() + .cloned() + .unwrap_or_else(|| target.name.clone()); + let data = inner.receive(mime_name, server_state); + if data.len() > max_req_bytes { + if let Err(e) = connection.send_and_check_request(&x::ChangeWindowAttributes { + window: request.requestor(), + value_list: &[x::Cw::EventMask(x::EventMask::PROPERTY_CHANGE)], + }) { + warn!("Failed to set up property change notifications: {e:?}"); + return false; + } + if let Err(e) = connection.send_and_check_request(&x::ChangeProperty { + mode: x::PropMode::Replace, + window: request.requestor(), + property: request.property(), + r#type: atoms.incr, + data: &[data.len() as u32], + }) { + warn!("Failed to set incr property for large transfer: {e:?}"); + return false; + } + debug!( + "beginning incr for {}", + get_atom_name(connection, target.atom) + ); + *incr_data = Some(WaylandIncrInfo { + range: 0..data.len(), + data, + target_window: request.requestor(), + property: request.property(), + target_type: target.atom, + max_req_bytes, + }); + true + } else { + match connection.send_and_check_request(&x::ChangeProperty { + mode: x::PropMode::Replace, + window: request.requestor(), + property: request.property(), + r#type: target.atom, + data: &data, + }) { + Ok(_) => true, + Err(e) => { + warn!("Failed setting selection property: {e:?}"); + false } } } @@ -665,7 +657,7 @@ impl XState { self.selection_state .primary .set_owner(&self.connection, self.wm_window); - debug!("Primaryset from Wayland"); + debug!("Primary set from Wayland"); } pub(super) fn handle_selection_event( @@ -771,15 +763,17 @@ impl XState { return true; } - data.handle_selection_request( + if data.handle_selection_request( &self.connection, &self.atoms, e, self.max_req_bytes, - &success, - &refuse, server_state, - ); + ) { + success() + } else { + refuse() + } } xcb::Event::XFixes(xcb::xfixes::Event::SelectionNotify(e)) => match e.selection() { diff --git a/tests/integration.rs b/tests/integration.rs index 4f422eb..b56eb64 100644 --- a/tests/integration.rs +++ b/tests/integration.rs @@ -1520,12 +1520,7 @@ fn incr_copy_from_x11() { let request = connection.await_selection_request(); assert_eq!(request.target(), connection.atoms.mime1); - connection - .send_and_check_request(&x::ChangeWindowAttributes { - window: request.requestor(), - value_list: &[x::Cw::EventMask(x::EventMask::PROPERTY_CHANGE)], - }) - .unwrap(); + connection.get_property_change_events(request.requestor()); connection.set_property( request.requestor(), connection.atoms.incr, @@ -1534,10 +1529,7 @@ fn incr_copy_from_x11() { ); connection.send_selection_notify(&request); // skip NewValue - let notify = match connection.poll_for_event().unwrap() { - Some(xcb::Event::X(x::Event::PropertyNotify(p))) => p, - other => panic!("Didn't get property notify event, instead got {other:?}"), - }; + let notify = connection.await_property_notify(); assert_eq!(notify.atom(), request.property()); assert_eq!(notify.state(), x::Property::NewValue); request.property() @@ -1555,11 +1547,7 @@ fn incr_copy_from_x11() { } assert_ne!(destination_property, x::Atom::none()); - let notify = match connection.await_event() { - xcb::Event::X(x::Event::PropertyNotify(p)) => p, - other => panic!("Didn't get property notify event, instead got {other:?}"), - }; - + let notify = connection.await_property_notify(); match it.next() { Some((idx, chunk)) => { assert_eq!(notify.atom(), destination_property, "chunk {idx}"); @@ -1572,10 +1560,7 @@ fn incr_copy_from_x11() { ); testwl.dispatch(); // skip NewValue - let notify = match connection.poll_for_event().unwrap() { - Some(xcb::Event::X(x::Event::PropertyNotify(p))) => p, - other => panic!("Didn't get property notify event, instead got {other:?}"), - }; + let notify = connection.await_property_notify(); assert_eq!(notify.atom(), destination_property, "chunk {idx}"); assert_eq!(notify.state(), x::Property::NewValue, "chunk {idx}"); false diff --git a/testwl/src/lib.rs b/testwl/src/lib.rs index 93e131e..b79f367 100644 --- a/testwl/src/lib.rs +++ b/testwl/src/lib.rs @@ -669,48 +669,38 @@ impl Server { }; let mut ret = Vec::new(); - let mut try_transfer = - |pending_ret: &mut PendingRet, mime: String, mut pending: PendingData| { - self.display.flush_clients().unwrap(); - let transfer_complete = send_data_for_mime(&mime, self); - if transfer_complete { - pending.rx.read_to_end(&mut pending.data).unwrap(); - ret.push(PasteData { - mime_type: mime, - data: pending.data, - }); - } else { - loop { - match pending.rx.read(&mut pending.data) { - Ok(0) => break, - Ok(_) => {} - Err(e) if e.kind() == std::io::ErrorKind::WouldBlock => break, - Err(e) => panic!("Failed reading data for mime {mime}: {e:?}"), - } - } - pending_ret.push((mime, Some(pending))); - self.dispatch(); - } - }; - while let Some((mime, pending)) = pending_ret.pop() { - match pending { - Some(pending) => try_transfer(&mut pending_ret, mime, pending), - None => { - let (rx, tx) = rustix::pipe::pipe().unwrap(); - send_selection(mime.clone(), tx.as_fd()); - drop(tx); + let mut pending = pending.unwrap_or_else(|| { + let (rx, tx) = rustix::pipe::pipe().unwrap(); + send_selection(mime.clone(), tx.as_fd()); + drop(tx); - let rx = std::fs::File::from(rx); - try_transfer( - &mut pending_ret, - mime, - PendingData { - rx, - data: Vec::new(), - }, - ); + let rx = std::fs::File::from(rx); + PendingData { + rx, + data: Vec::new(), } + }); + + self.display.flush_clients().unwrap(); + let transfer_complete = send_data_for_mime(&mime, self); + if transfer_complete { + pending.rx.read_to_end(&mut pending.data).unwrap(); + ret.push(PasteData { + mime_type: mime, + data: pending.data, + }); + } else { + loop { + match pending.rx.read(&mut pending.data) { + Ok(0) => break, + Ok(_) => {} + Err(e) if e.kind() == std::io::ErrorKind::WouldBlock => break, + Err(e) => panic!("Failed reading data for mime {mime}: {e:?}"), + } + } + pending_ret.push((mime, Some(pending))); + self.dispatch(); } }