data_device: support for custom DnD icons

This commit is contained in:
Victor Berger 2018-11-22 16:02:01 +01:00 committed by Victor Berger
parent f3a68fb1af
commit 280decf863
5 changed files with 119 additions and 22 deletions

View File

@ -9,6 +9,7 @@ use rand;
use smithay::{ use smithay::{
wayland::{ wayland::{
compositor::{compositor_init, CompositorToken, SurfaceAttributes, SurfaceEvent}, compositor::{compositor_init, CompositorToken, SurfaceAttributes, SurfaceEvent},
data_device::DnDIconRole,
seat::CursorImageRole, seat::CursorImageRole,
shell::{ shell::{
legacy::{ legacy::{
@ -31,6 +32,7 @@ use window_map::{Kind as SurfaceKind, WindowMap};
define_roles!(Roles => define_roles!(Roles =>
[ XdgSurface, XdgSurfaceRole ] [ XdgSurface, XdgSurfaceRole ]
[ ShellSurface, ShellSurfaceRole<()>] [ ShellSurface, ShellSurfaceRole<()>]
[ DnDIcon, DnDIconRole ]
[ CursorImage, CursorImageRole ] [ CursorImage, CursorImageRole ]
); );

View File

@ -140,6 +140,7 @@ pub fn run_udev(mut display: Display, mut event_loop: EventLoop<()>, log: Logger
&mut display.borrow_mut(), &mut display.borrow_mut(),
|_| {}, |_| {},
default_action_chooser, default_action_chooser,
compositor_token.clone(),
log.clone(), log.clone(),
); );

View File

@ -50,7 +50,13 @@ pub fn run_winit(display: &mut Display, event_loop: &mut EventLoop<()>, log: Log
let (compositor_token, _, _, window_map) = init_shell(display, log.clone()); let (compositor_token, _, _, window_map) = init_shell(display, log.clone());
init_data_device(display, |_| {}, default_action_chooser, log.clone()); init_data_device(
display,
|_| {},
default_action_chooser,
compositor_token.clone(),
log.clone(),
);
let (mut seat, _) = Seat::new(display, "winit".into(), compositor_token.clone(), log.clone()); let (mut seat, _) = Seat::new(display, "winit".into(), compositor_token.clone(), log.clone());

View File

@ -8,37 +8,49 @@ use wayland_server::{
NewResource, Resource, NewResource, Resource,
}; };
use wayland::seat::{AxisFrame, PointerGrab, PointerInnerHandle, Seat}; use wayland::{
compositor::{roles::Role, CompositorToken},
seat::{AxisFrame, PointerGrab, PointerInnerHandle, Seat},
};
use super::{with_source_metadata, DataDeviceData, SeatData}; use super::{with_source_metadata, DataDeviceData, DnDIconRole, SeatData};
pub(crate) struct DnDGrab { pub(crate) struct DnDGrab<U, R> {
data_source: Option<Resource<wl_data_source::WlDataSource>>, data_source: Option<Resource<wl_data_source::WlDataSource>>,
current_focus: Option<Resource<wl_surface::WlSurface>>, current_focus: Option<Resource<wl_surface::WlSurface>>,
pending_offers: Vec<Resource<wl_data_offer::WlDataOffer>>, pending_offers: Vec<Resource<wl_data_offer::WlDataOffer>>,
offer_data: Option<Arc<Mutex<OfferData>>>, offer_data: Option<Arc<Mutex<OfferData>>>,
icon: Option<Resource<wl_surface::WlSurface>>,
origin: Resource<wl_surface::WlSurface>, origin: Resource<wl_surface::WlSurface>,
callback: Arc<Mutex<FnMut(super::DataDeviceEvent) + Send>>,
token: CompositorToken<U, R>,
seat: Seat, seat: Seat,
} }
impl DnDGrab { impl<U: 'static, R: Role<DnDIconRole> + 'static> DnDGrab<U, R> {
pub(crate) fn new( pub(crate) fn new(
source: Option<Resource<wl_data_source::WlDataSource>>, source: Option<Resource<wl_data_source::WlDataSource>>,
origin: Resource<wl_surface::WlSurface>, origin: Resource<wl_surface::WlSurface>,
seat: Seat, seat: Seat,
) -> DnDGrab { icon: Option<Resource<wl_surface::WlSurface>>,
token: CompositorToken<U, R>,
callback: Arc<Mutex<FnMut(super::DataDeviceEvent) + Send>>,
) -> DnDGrab<U, R> {
DnDGrab { DnDGrab {
data_source: source, data_source: source,
current_focus: None, current_focus: None,
pending_offers: Vec::with_capacity(1), pending_offers: Vec::with_capacity(1),
offer_data: None, offer_data: None,
origin, origin,
icon,
callback,
token,
seat, seat,
} }
} }
} }
impl PointerGrab for DnDGrab { impl<U: 'static, R: Role<DnDIconRole> + 'static> PointerGrab for DnDGrab<U, R> {
fn motion( fn motion(
&mut self, &mut self,
_handle: &mut PointerInnerHandle, _handle: &mut PointerInnerHandle,
@ -215,6 +227,12 @@ impl PointerGrab for DnDGrab {
source.send(wl_data_source::Event::Cancelled); source.send(wl_data_source::Event::Cancelled);
} }
} }
(&mut *self.callback.lock().unwrap())(super::DataDeviceEvent::DnDDropped);
if let Some(icon) = self.icon.take() {
if icon.is_alive() {
self.token.remove_role::<super::DnDIconRole>(&icon).unwrap();
}
}
// in all cases abandon the drop // in all cases abandon the drop
// no more buttons are pressed, release the grab // no more buttons are pressed, release the grab
handle.unset_grab(serial, time); handle.unset_grab(serial, time);

View File

@ -24,23 +24,33 @@
//! - the freestanding function `start_dnd` allows you to initiate a drag'n'drop event from the compositor //! - the freestanding function `start_dnd` allows you to initiate a drag'n'drop event from the compositor
//! itself and receive interactions of clients with it via an other dedicated callback. //! itself and receive interactions of clients with it via an other dedicated callback.
//! //!
//! The module also defines the `DnDIconRole` that you need to insert into your compositor roles enum, to
//! represent surfaces that are used as a DnD icon.
//!
//! ## Initialization //! ## Initialization
//! //!
//! ``` //! ```
//! # extern crate wayland_server; //! # extern crate wayland_server;
//! # #[macro_use] extern crate smithay; //! # #[macro_use] extern crate smithay;
//! use smithay::wayland::data_device::{init_data_device, default_action_chooser}; //! use smithay::wayland::data_device::{init_data_device, default_action_chooser, DnDIconRole};
//! # use smithay::wayland::compositor::compositor_init;
//!
//! // You need to insert the `DndIconRole` into your roles, to handle requests from clients
//! // to set a surface as a dnd icon
//! define_roles!(Roles => [DnDIcon, DnDIconRole]);
//! //!
//! # fn main(){ //! # fn main(){
//! # let mut event_loop = wayland_server::calloop::EventLoop::<()>::new().unwrap(); //! # let mut event_loop = wayland_server::calloop::EventLoop::<()>::new().unwrap();
//! # let mut display = wayland_server::Display::new(event_loop.handle()); //! # let mut display = wayland_server::Display::new(event_loop.handle());
//! # let (compositor_token, _, _) = compositor_init::<(), Roles, _, _>(&mut display, |_, _, _| {}, None);
//! // init the data device: //! // init the data device:
//! init_data_device( //! init_data_device(
//! &mut display, // the display //! &mut display, // the display
//! |dnd_event| { /* a callback to react to client DnD/selection actions */ }, //! |dnd_event| { /* a callback to react to client DnD/selection actions */ },
//! default_action_chooser, // a closure to choose the DnD action depending on clients //! default_action_chooser, // a closure to choose the DnD action depending on clients
//! // negociation //! // negociation
//! None // insert a logger here //! compositor_token.clone(), // a compositor token
//! None // insert a logger here
//! ); //! );
//! # } //! # }
//! ``` //! ```
@ -52,12 +62,15 @@ use wayland_server::{
protocol::{ protocol::{
wl_data_device, wl_data_device,
wl_data_device_manager::{self, DndAction}, wl_data_device_manager::{self, DndAction},
wl_data_offer, wl_data_source, wl_data_offer, wl_data_source, wl_surface,
}, },
Client, Display, Global, NewResource, Resource, Client, Display, Global, NewResource, Resource,
}; };
use wayland::seat::Seat; use wayland::{
compositor::{roles::Role, CompositorToken},
seat::Seat,
};
mod data_source; mod data_source;
mod dnd_grab; mod dnd_grab;
@ -71,7 +84,22 @@ pub enum DataDeviceEvent {
/// A client has set the selection /// A client has set the selection
NewSelection(Option<Resource<wl_data_source::WlDataSource>>), NewSelection(Option<Resource<wl_data_source::WlDataSource>>),
/// A client started a drag'n'drop as response to a user pointer action /// A client started a drag'n'drop as response to a user pointer action
DnDStarted(Option<Resource<wl_data_source::WlDataSource>>), DnDStarted {
/// The data source provided by the client
///
/// If it is `None`, this means the DnD is restricted to surfaces of the
/// same client and the client will manage data transfert by itself.
source: Option<Resource<wl_data_source::WlDataSource>>,
/// The icon the client requested to be used to be associated with the cursor icon
/// during the drag'n'drop.
icon: Option<Resource<wl_surface::WlSurface>>,
},
/// The drag'n'drop action was finished by the user releasing the buttons
///
/// At this point, any pointer icon should be removed.
///
/// Note that this event will only be genrated for client-initiated drag'n'drop session.
DnDDropped,
/// A client requested to read the server-set selection /// A client requested to read the server-set selection
SendSelection { SendSelection {
/// the requested mime type /// the requested mime type
@ -81,6 +109,10 @@ pub enum DataDeviceEvent {
}, },
} }
/// The role applied to surfaces used as DnD icons
#[derive(Default)]
pub struct DnDIconRole;
enum Selection { enum Selection {
Empty, Empty,
Client(Resource<wl_data_source::WlDataSource>), Client(Resource<wl_data_source::WlDataSource>),
@ -246,22 +278,31 @@ impl SeatData {
/// available actions (which is the intersection of the actions supported by the source and targets) /// 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 /// 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()`. /// chosen (and thus the drag'n'drop should abort on drop), return `DndAction::empty()`.
pub fn init_data_device<F, C, L>( pub fn init_data_device<F, C, U, R, L>(
display: &mut Display, display: &mut Display,
callback: C, callback: C,
action_choice: F, action_choice: F,
token: CompositorToken<U, R>,
logger: L, logger: L,
) -> Global<wl_data_device_manager::WlDataDeviceManager> ) -> Global<wl_data_device_manager::WlDataDeviceManager>
where where
F: FnMut(DndAction, DndAction) -> DndAction + Send + 'static, F: FnMut(DndAction, DndAction) -> DndAction + Send + 'static,
C: FnMut(DataDeviceEvent) + Send + 'static, C: FnMut(DataDeviceEvent) + Send + 'static,
R: Role<DnDIconRole> + 'static,
U: 'static,
L: Into<Option<::slog::Logger>>, L: Into<Option<::slog::Logger>>,
{ {
let log = ::slog_or_stdlog(logger).new(o!("smithay_module" => "data_device_mgr")); let log = ::slog_or_stdlog(logger).new(o!("smithay_module" => "data_device_mgr"));
let action_choice = Arc::new(Mutex::new(action_choice)); let action_choice = Arc::new(Mutex::new(action_choice));
let callback = Arc::new(Mutex::new(callback)); let callback = Arc::new(Mutex::new(callback));
let global = display.create_global(3, move |new_ddm, _version| { let global = display.create_global(3, move |new_ddm, _version| {
implement_ddm(new_ddm, callback.clone(), action_choice.clone(), log.clone()); implement_ddm(
new_ddm,
callback.clone(),
action_choice.clone(),
token,
log.clone(),
);
}); });
global global
@ -330,15 +371,18 @@ where
} }
} }
fn implement_ddm<F, C>( fn implement_ddm<F, C, U, R>(
new_ddm: NewResource<wl_data_device_manager::WlDataDeviceManager>, new_ddm: NewResource<wl_data_device_manager::WlDataDeviceManager>,
callback: Arc<Mutex<C>>, callback: Arc<Mutex<C>>,
action_choice: Arc<Mutex<F>>, action_choice: Arc<Mutex<F>>,
token: CompositorToken<U, R>,
log: ::slog::Logger, log: ::slog::Logger,
) -> Resource<wl_data_device_manager::WlDataDeviceManager> ) -> Resource<wl_data_device_manager::WlDataDeviceManager>
where where
F: FnMut(DndAction, DndAction) -> DndAction + Send + 'static, F: FnMut(DndAction, DndAction) -> DndAction + Send + 'static,
C: FnMut(DataDeviceEvent) + Send + 'static, C: FnMut(DataDeviceEvent) + Send + 'static,
R: Role<DnDIconRole> + 'static,
U: 'static,
{ {
use self::wl_data_device_manager::Request; use self::wl_data_device_manager::Request;
new_ddm.implement( new_ddm.implement(
@ -357,6 +401,7 @@ where
seat.clone(), seat.clone(),
callback.clone(), callback.clone(),
action_choice.clone(), action_choice.clone(),
token.clone(),
log.clone(), log.clone(),
); );
seat_data.lock().unwrap().known_devices.push(data_device); seat_data.lock().unwrap().known_devices.push(data_device);
@ -376,16 +421,19 @@ struct DataDeviceData {
action_choice: Arc<Mutex<FnMut(DndAction, DndAction) -> DndAction + Send + 'static>>, action_choice: Arc<Mutex<FnMut(DndAction, DndAction) -> DndAction + Send + 'static>>,
} }
fn implement_data_device<F, C>( fn implement_data_device<F, C, U, R>(
new_dd: NewResource<wl_data_device::WlDataDevice>, new_dd: NewResource<wl_data_device::WlDataDevice>,
seat: Seat, seat: Seat,
callback: Arc<Mutex<C>>, callback: Arc<Mutex<C>>,
action_choice: Arc<Mutex<F>>, action_choice: Arc<Mutex<F>>,
token: CompositorToken<U, R>,
log: ::slog::Logger, log: ::slog::Logger,
) -> Resource<wl_data_device::WlDataDevice> ) -> Resource<wl_data_device::WlDataDevice>
where where
F: FnMut(DndAction, DndAction) -> DndAction + Send + 'static, F: FnMut(DndAction, DndAction) -> DndAction + Send + 'static,
C: FnMut(DataDeviceEvent) + Send + 'static, C: FnMut(DataDeviceEvent) + Send + 'static,
R: Role<DnDIconRole> + 'static,
U: 'static,
{ {
use self::wl_data_device::Request; use self::wl_data_device::Request;
let dd_data = DataDeviceData { let dd_data = DataDeviceData {
@ -397,15 +445,37 @@ where
Request::StartDrag { Request::StartDrag {
source, source,
origin, origin,
icon: _, icon,
serial, serial,
} => { } => {
/* TODO: handle the icon */ /* TODO: handle the icon */
if let Some(pointer) = seat.get_pointer() { if let Some(pointer) = seat.get_pointer() {
if pointer.has_grab(serial) { if pointer.has_grab(serial) {
if let Some(ref icon) = icon {
if token.give_role::<DnDIconRole>(icon).is_err() {
dd.post_error(
wl_data_device::Error::Role as u32,
"Given surface already has an other role".into(),
);
return;
}
}
// The StartDrag is in response to a pointer implicit grab, all is good // The StartDrag is in response to a pointer implicit grab, all is good
(&mut *callback.lock().unwrap())(DataDeviceEvent::DnDStarted(source.clone())); (&mut *callback.lock().unwrap())(DataDeviceEvent::DnDStarted {
pointer.set_grab(dnd_grab::DnDGrab::new(source, origin, seat.clone()), serial); source: source.clone(),
icon: icon.clone(),
});
pointer.set_grab(
dnd_grab::DnDGrab::new(
source,
origin,
seat.clone(),
icon.clone(),
token.clone(),
callback.clone(),
),
serial,
);
return; return;
} }
} }