From d41517f85b4e8bc98e10d24459587847fe010fb0 Mon Sep 17 00:00:00 2001 From: Victor Berger Date: Wed, 21 Nov 2018 22:24:18 +0100 Subject: [PATCH] data_device: let the compositor interact with the selection --- anvil/src/udev.rs | 7 +- anvil/src/winit.rs | 2 +- src/wayland/data_device/mod.rs | 113 +++++++++++++++++++++++++++++++-- 3 files changed, 113 insertions(+), 9 deletions(-) diff --git a/anvil/src/udev.rs b/anvil/src/udev.rs index 39d69f5..4e6dacd 100644 --- a/anvil/src/udev.rs +++ b/anvil/src/udev.rs @@ -108,7 +108,12 @@ pub fn run_udev(mut display: Display, mut event_loop: EventLoop<()>, log: Logger let udev_session_id = notifier.register(&mut udev_backend); - init_data_device(&mut display.borrow_mut(), ::misc::dnd_action_chooser, log.clone()); + init_data_device( + &mut display.borrow_mut(), + |_| {}, + ::misc::dnd_action_chooser, + log.clone(), + ); let (mut w_seat, _) = Seat::new(&mut display.borrow_mut(), session.seat(), log.clone()); diff --git a/anvil/src/winit.rs b/anvil/src/winit.rs index ecb3ff4..0ef1b76 100644 --- a/anvil/src/winit.rs +++ b/anvil/src/winit.rs @@ -54,7 +54,7 @@ pub fn run_winit(display: &mut Display, event_loop: &mut EventLoop<()>, log: Log let (compositor_token, _, _, window_map) = init_shell(display, log.clone()); - init_data_device(display, ::misc::dnd_action_chooser, log.clone()); + init_data_device(display, |_| {}, ::misc::dnd_action_chooser, log.clone()); let (mut seat, _) = Seat::new(display, "winit".into(), log.clone()); diff --git a/src/wayland/data_device/mod.rs b/src/wayland/data_device/mod.rs index 7a15564..f76731c 100644 --- a/src/wayland/data_device/mod.rs +++ b/src/wayland/data_device/mod.rs @@ -1,3 +1,4 @@ +use std::os::unix::io::RawFd; use std::sync::{Arc, Mutex}; use wayland_server::{ @@ -16,9 +17,25 @@ mod dnd_grab; pub use self::data_source::{with_source_metadata, SourceMetadata}; +/// Events that are generated by interactions of the clients with the data device +pub enum DataDeviceEvent { + /// A client has set the selection + NewSelection(Option>), + /// A client started a drag'n'drop as response to a user pointer action + DnDStarted(Option>), + /// A client requested to read the server-set selection + SendSelection { + // the requested mime type + mime_type: String, + // the fd to write into + fd: RawFd, + }, +} + enum Selection { Empty, Client(Resource), + Compositor(SourceMetadata), } struct SeatData { @@ -109,6 +126,50 @@ impl SeatData { dd.send(wl_data_device::Event::Selection { id: Some(offer) }); } } + Selection::Compositor(ref meta) => { + for dd in &self.known_devices { + // skip data devices not belonging to our client + if dd.client().map(|c| !c.equals(client)).unwrap_or(true) { + continue; + } + let log = self.log.clone(); + let offer_meta = meta.clone(); + let callback = dd + .user_data::>>() + .unwrap() + .clone(); + // create a corresponding data offer + let offer = client + .create_resource::(dd.version()) + .unwrap() + .implement( + move |req, _offer| match req { + wl_data_offer::Request::Receive { fd, mime_type } => { + // check if the associated mime type is valid + if !offer_meta.mime_types.contains(&mime_type) { + // deny the receive + debug!(log, "Denying a wl_data_offer.receive with invalid source."); + let _ = ::nix::unistd::close(fd); + } else { + (&mut *callback.lock().unwrap())(DataDeviceEvent::SendSelection { + mime_type, + fd, + }); + } + } + _ => { /* seleciton data offers only care about the `receive` event */ } + }, + None::, + (), + ); + // advertize the offer to the client + dd.send(wl_data_device::Event::DataOffer { id: offer.clone() }); + for mime_type in meta.mime_types.iter().cloned() { + offer.send(wl_data_offer::Event::Offer { mime_type }) + } + dd.send(wl_data_device::Event::Selection { id: Some(offer) }); + } + } } } } @@ -131,19 +192,22 @@ impl SeatData { /// available actions (which is the intersection of the actions supported by the source and targets) /// and the second argument is the preferred action reported by the target. If no action should be /// chosen (and thus the drag'n'drop should abort on drop), return `DndAction::empty()`. -pub fn init_data_device( +pub fn init_data_device( display: &mut Display, + callback: C, action_choice: F, logger: L, ) -> Global where F: FnMut(DndAction, DndAction) -> DndAction + Send + 'static, + C: FnMut(DataDeviceEvent) + Send + 'static, L: Into>, { let log = ::slog_or_stdlog(logger).new(o!("smithay_module" => "data_device_mgr")); let action_choice = Arc::new(Mutex::new(action_choice)); + let callback = Arc::new(Mutex::new(callback)); let global = display.create_global(3, move |new_ddm, _version| { - implement_ddm(new_ddm, action_choice.clone(), log.clone()); + implement_ddm(new_ddm, callback.clone(), action_choice.clone(), log.clone()); }); global @@ -165,13 +229,38 @@ pub fn set_data_device_focus(seat: &Seat, client: Option) { seat_data.lock().unwrap().set_focus(client); } -fn implement_ddm( +/// Set a compositor-provided selection for this seat +/// +/// You need to provide the available mime types for this selection. +/// +/// Whenever a client requests to read the selection, your callback will +/// receive a `DataDeviceEvent::SendSelection` event. +pub fn set_data_device_selection(seat: &Seat, mime_types: Vec) { + // TODO: same question as in set_data_device_focus + seat.user_data().insert_if_missing(|| { + Mutex::new(SeatData::new( + seat.arc.log.new(o!("smithay_module" => "data_device_mgr")), + )) + }); + let seat_data = seat.user_data().get::>().unwrap(); + seat_data + .lock() + .unwrap() + .set_selection(Selection::Compositor(SourceMetadata { + mime_types, + dnd_action: DndAction::empty(), + })); +} + +fn implement_ddm( new_ddm: NewResource, + callback: Arc>, action_choice: Arc>, log: ::slog::Logger, ) -> Resource where F: FnMut(DndAction, DndAction) -> DndAction + Send + 'static, + C: FnMut(DataDeviceEvent) + Send + 'static, { use self::wl_data_device_manager::Request; new_ddm.implement( @@ -185,8 +274,13 @@ where seat.user_data() .insert_if_missing(|| Mutex::new(SeatData::new(log.clone()))); let seat_data = seat.user_data().get::>().unwrap(); - let data_device = - implement_data_device(id, seat.clone(), action_choice.clone(), log.clone()); + let data_device = implement_data_device( + id, + seat.clone(), + callback.clone(), + action_choice.clone(), + log.clone(), + ); seat_data.lock().unwrap().known_devices.push(data_device); } None => { @@ -199,16 +293,19 @@ where ) } -fn implement_data_device( +fn implement_data_device( new_dd: NewResource, seat: Seat, + callback: Arc>, action_choice: Arc>, log: ::slog::Logger, ) -> Resource where F: FnMut(DndAction, DndAction) -> DndAction + Send + 'static, + C: FnMut(DataDeviceEvent) + Send + 'static, { use self::wl_data_device::Request; + let callback2 = callback.clone(); new_dd.implement( move |req, dd| match req { Request::StartDrag { @@ -221,6 +318,7 @@ where if let Some(pointer) = seat.get_pointer() { if pointer.has_grab(serial) { // The StartDrag is in response to a pointer implicit grab, all is good + (&mut *callback.lock().unwrap())(DataDeviceEvent::DnDStarted(source.clone())); pointer.set_grab( dnd_grab::DnDGrab::new(source, origin, seat.clone(), action_choice.clone()), serial, @@ -239,6 +337,7 @@ where .unwrap_or(false) { let seat_data = seat.user_data().get::>().unwrap(); + (&mut *callback.lock().unwrap())(DataDeviceEvent::NewSelection(source.clone())); // The client has kbd focus, it can set the selection seat_data .lock() @@ -261,6 +360,6 @@ where } }, None::, - (), + callback2 as Arc>, ) }