diff --git a/src/xstate/selection.rs b/src/xstate/selection.rs index 7b39a94..cb7ca8b 100644 --- a/src/xstate/selection.rs +++ b/src/xstate/selection.rs @@ -12,6 +12,7 @@ use xcb::x; struct SelectionTargetId { name: String, atom: x::Atom, + source: Option, } struct PendingSelectionData { @@ -227,10 +228,18 @@ impl XState { } pub(crate) fn set_clipboard(&mut self, selection: ForeignSelection) { - let mimes = selection + let mut utf8_xwl = false; + let mut utf8_wl = false; + let mut mimes: Vec = selection .mime_types .iter() .map(|mime| { + match mime.as_str() { + "UTF8_STRING" => utf8_xwl = true, + "text/plain;charset=utf-8" => utf8_wl = true, + _ => {} + } + let atom = self .connection .wait_for_reply(self.connection.send_request(&x::InternAtom { @@ -242,10 +251,28 @@ impl XState { SelectionTargetId { name: mime.clone(), atom: atom.atom(), + source: None, } }) .collect(); + if utf8_wl && !utf8_xwl { + let name = "UTF8_STRING".to_string(); + let atom = self + .connection + .wait_for_reply(self.connection.send_request(&x::InternAtom { + only_if_exists: false, + name: name.as_bytes(), + })) + .unwrap() + .atom(); + mimes.push(SelectionTargetId { + name, + atom, + source: Some("text/plain;charset=utf-8".to_string()), + }); + } + self.selection_data.current_selection = Some(CurrentSelection::Wayland { mimes, inner: selection, @@ -364,7 +391,12 @@ impl XState { return true; }; - let data = inner.receive(target.name.clone(), server_state); + let mime_name = target + .source + .as_ref() + .cloned() + .unwrap_or_else(|| target.name.clone()); + let data = inner.receive(mime_name, server_state); match self.connection.send_and_check_request(&x::ChangeProperty { mode: x::PropMode::Replace, window: e.requestor(), @@ -479,6 +511,7 @@ impl XState { .map(|target_atom| SelectionTargetId { name: get_atom_name(&self.connection, target_atom), atom: target_atom, + source: None, }) .collect(); diff --git a/tests/integration.rs b/tests/integration.rs index 2d2f348..1836374 100644 --- a/tests/integration.rs +++ b/tests/integration.rs @@ -1306,3 +1306,59 @@ fn wayland_then_x11_clipboard_owner() { assert_eq!(request.selection(), connection.atoms.clipboard); assert_eq!(request.target(), connection.atoms.targets); } + +#[test] +fn fake_selection_targets() { + let mut f = Fixture::new(); + let mut connection = Connection::new(&f.display); + let window = connection.new_window(connection.root, 0, 0, 20, 20, false); + connection.get_selection_owner_change_events(true, window); + + let data = b"boingloings"; + f.map_as_toplevel(&mut connection, window); + let offer = vec![testwl::PasteData { + mime_type: "text/plain;charset=utf-8".into(), + data: data.to_vec(), + }]; + f.testwl.create_data_offer(offer.clone()); + + connection.await_selection_owner_change(); + connection.verify_clipboard_owner(connection.wm_window); + connection.get_selection_owner_change_events(false, window); + + let utf8_string = connection + .get_reply(&x::InternAtom { + only_if_exists: false, + name: b"UTF8_STRING", + }) + .atom(); + + connection + .send_and_check_request(&x::ConvertSelection { + requestor: window, + selection: connection.atoms.clipboard, + target: utf8_string, + property: utf8_string, + time: x::CURRENT_TIME, + }) + .unwrap(); + + f.wait_and_dispatch(); + let notify = connection.await_selection_notify(); + assert_eq!(notify.property(), utf8_string, "ConvertSelection failed"); + + let reply = connection.get_reply(&x::GetProperty { + delete: false, + window, + property: utf8_string, + r#type: utf8_string, + long_offset: 0, + long_length: data.len() as u32, + }); + let paste_data: &[u8] = reply.value(); + + assert_eq!( + std::str::from_utf8(paste_data).unwrap(), + std::str::from_utf8(data).unwrap() + ); +} diff --git a/testwl/src/lib.rs b/testwl/src/lib.rs index 83510ae..dde7cfa 100644 --- a/testwl/src/lib.rs +++ b/testwl/src/lib.rs @@ -875,7 +875,7 @@ impl Dispatch> for State { let pos = data .iter() .position(|data| data.mime_type == mime_type) - .expect("Invalid mime type: {mime_type}"); + .unwrap_or_else(|| panic!("Invalid mime type: {mime_type}")); let mut stream = UnixStream::from(fd); stream.write_all(&data[pos].data).unwrap();