data_device: support for custom DnD icons
This commit is contained in:
parent
f3a68fb1af
commit
280decf863
|
@ -9,6 +9,7 @@ use rand;
|
|||
use smithay::{
|
||||
wayland::{
|
||||
compositor::{compositor_init, CompositorToken, SurfaceAttributes, SurfaceEvent},
|
||||
data_device::DnDIconRole,
|
||||
seat::CursorImageRole,
|
||||
shell::{
|
||||
legacy::{
|
||||
|
@ -31,6 +32,7 @@ use window_map::{Kind as SurfaceKind, WindowMap};
|
|||
define_roles!(Roles =>
|
||||
[ XdgSurface, XdgSurfaceRole ]
|
||||
[ ShellSurface, ShellSurfaceRole<()>]
|
||||
[ DnDIcon, DnDIconRole ]
|
||||
[ CursorImage, CursorImageRole ]
|
||||
);
|
||||
|
||||
|
|
|
@ -140,6 +140,7 @@ pub fn run_udev(mut display: Display, mut event_loop: EventLoop<()>, log: Logger
|
|||
&mut display.borrow_mut(),
|
||||
|_| {},
|
||||
default_action_chooser,
|
||||
compositor_token.clone(),
|
||||
log.clone(),
|
||||
);
|
||||
|
||||
|
|
|
@ -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());
|
||||
|
||||
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());
|
||||
|
||||
|
|
|
@ -8,37 +8,49 @@ use wayland_server::{
|
|||
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>>,
|
||||
current_focus: Option<Resource<wl_surface::WlSurface>>,
|
||||
pending_offers: Vec<Resource<wl_data_offer::WlDataOffer>>,
|
||||
offer_data: Option<Arc<Mutex<OfferData>>>,
|
||||
icon: Option<Resource<wl_surface::WlSurface>>,
|
||||
origin: Resource<wl_surface::WlSurface>,
|
||||
callback: Arc<Mutex<FnMut(super::DataDeviceEvent) + Send>>,
|
||||
token: CompositorToken<U, R>,
|
||||
seat: Seat,
|
||||
}
|
||||
|
||||
impl DnDGrab {
|
||||
impl<U: 'static, R: Role<DnDIconRole> + 'static> DnDGrab<U, R> {
|
||||
pub(crate) fn new(
|
||||
source: Option<Resource<wl_data_source::WlDataSource>>,
|
||||
origin: Resource<wl_surface::WlSurface>,
|
||||
seat: Seat,
|
||||
) -> DnDGrab {
|
||||
icon: Option<Resource<wl_surface::WlSurface>>,
|
||||
token: CompositorToken<U, R>,
|
||||
callback: Arc<Mutex<FnMut(super::DataDeviceEvent) + Send>>,
|
||||
) -> DnDGrab<U, R> {
|
||||
DnDGrab {
|
||||
data_source: source,
|
||||
current_focus: None,
|
||||
pending_offers: Vec::with_capacity(1),
|
||||
offer_data: None,
|
||||
origin,
|
||||
icon,
|
||||
callback,
|
||||
token,
|
||||
seat,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PointerGrab for DnDGrab {
|
||||
impl<U: 'static, R: Role<DnDIconRole> + 'static> PointerGrab for DnDGrab<U, R> {
|
||||
fn motion(
|
||||
&mut self,
|
||||
_handle: &mut PointerInnerHandle,
|
||||
|
@ -215,6 +227,12 @@ impl PointerGrab for DnDGrab {
|
|||
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
|
||||
// no more buttons are pressed, release the grab
|
||||
handle.unset_grab(serial, time);
|
||||
|
|
|
@ -24,23 +24,33 @@
|
|||
//! - 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.
|
||||
//!
|
||||
//! 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
|
||||
//!
|
||||
//! ```
|
||||
//! # extern crate wayland_server;
|
||||
//! # #[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(){
|
||||
//! # let mut event_loop = wayland_server::calloop::EventLoop::<()>::new().unwrap();
|
||||
//! # let mut display = wayland_server::Display::new(event_loop.handle());
|
||||
//! # let (compositor_token, _, _) = compositor_init::<(), Roles, _, _>(&mut display, |_, _, _| {}, None);
|
||||
//! // init the data device:
|
||||
//! init_data_device(
|
||||
//! &mut display, // the display
|
||||
//! &mut display, // the display
|
||||
//! |dnd_event| { /* a callback to react to client DnD/selection actions */ },
|
||||
//! default_action_chooser, // a closure to choose the DnD action depending on clients
|
||||
//! // negociation
|
||||
//! None // insert a logger here
|
||||
//! default_action_chooser, // a closure to choose the DnD action depending on clients
|
||||
//! // negociation
|
||||
//! compositor_token.clone(), // a compositor token
|
||||
//! None // insert a logger here
|
||||
//! );
|
||||
//! # }
|
||||
//! ```
|
||||
|
@ -52,12 +62,15 @@ use wayland_server::{
|
|||
protocol::{
|
||||
wl_data_device,
|
||||
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,
|
||||
};
|
||||
|
||||
use wayland::seat::Seat;
|
||||
use wayland::{
|
||||
compositor::{roles::Role, CompositorToken},
|
||||
seat::Seat,
|
||||
};
|
||||
|
||||
mod data_source;
|
||||
mod dnd_grab;
|
||||
|
@ -71,7 +84,22 @@ pub enum DataDeviceEvent {
|
|||
/// A client has set the selection
|
||||
NewSelection(Option<Resource<wl_data_source::WlDataSource>>),
|
||||
/// 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
|
||||
SendSelection {
|
||||
/// 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 {
|
||||
Empty,
|
||||
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)
|
||||
/// 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<F, C, L>(
|
||||
pub fn init_data_device<F, C, U, R, L>(
|
||||
display: &mut Display,
|
||||
callback: C,
|
||||
action_choice: F,
|
||||
token: CompositorToken<U, R>,
|
||||
logger: L,
|
||||
) -> Global<wl_data_device_manager::WlDataDeviceManager>
|
||||
where
|
||||
F: FnMut(DndAction, DndAction) -> DndAction + Send + 'static,
|
||||
C: FnMut(DataDeviceEvent) + Send + 'static,
|
||||
R: Role<DnDIconRole> + 'static,
|
||||
U: 'static,
|
||||
L: Into<Option<::slog::Logger>>,
|
||||
{
|
||||
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, callback.clone(), action_choice.clone(), log.clone());
|
||||
implement_ddm(
|
||||
new_ddm,
|
||||
callback.clone(),
|
||||
action_choice.clone(),
|
||||
token,
|
||||
log.clone(),
|
||||
);
|
||||
});
|
||||
|
||||
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>,
|
||||
callback: Arc<Mutex<C>>,
|
||||
action_choice: Arc<Mutex<F>>,
|
||||
token: CompositorToken<U, R>,
|
||||
log: ::slog::Logger,
|
||||
) -> Resource<wl_data_device_manager::WlDataDeviceManager>
|
||||
where
|
||||
F: FnMut(DndAction, DndAction) -> DndAction + Send + 'static,
|
||||
C: FnMut(DataDeviceEvent) + Send + 'static,
|
||||
R: Role<DnDIconRole> + 'static,
|
||||
U: 'static,
|
||||
{
|
||||
use self::wl_data_device_manager::Request;
|
||||
new_ddm.implement(
|
||||
|
@ -357,6 +401,7 @@ where
|
|||
seat.clone(),
|
||||
callback.clone(),
|
||||
action_choice.clone(),
|
||||
token.clone(),
|
||||
log.clone(),
|
||||
);
|
||||
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>>,
|
||||
}
|
||||
|
||||
fn implement_data_device<F, C>(
|
||||
fn implement_data_device<F, C, U, R>(
|
||||
new_dd: NewResource<wl_data_device::WlDataDevice>,
|
||||
seat: Seat,
|
||||
callback: Arc<Mutex<C>>,
|
||||
action_choice: Arc<Mutex<F>>,
|
||||
token: CompositorToken<U, R>,
|
||||
log: ::slog::Logger,
|
||||
) -> Resource<wl_data_device::WlDataDevice>
|
||||
where
|
||||
F: FnMut(DndAction, DndAction) -> DndAction + Send + 'static,
|
||||
C: FnMut(DataDeviceEvent) + Send + 'static,
|
||||
R: Role<DnDIconRole> + 'static,
|
||||
U: 'static,
|
||||
{
|
||||
use self::wl_data_device::Request;
|
||||
let dd_data = DataDeviceData {
|
||||
|
@ -397,15 +445,37 @@ where
|
|||
Request::StartDrag {
|
||||
source,
|
||||
origin,
|
||||
icon: _,
|
||||
icon,
|
||||
serial,
|
||||
} => {
|
||||
/* TODO: handle the icon */
|
||||
if let Some(pointer) = seat.get_pointer() {
|
||||
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
|
||||
(&mut *callback.lock().unwrap())(DataDeviceEvent::DnDStarted(source.clone()));
|
||||
pointer.set_grab(dnd_grab::DnDGrab::new(source, origin, seat.clone()), serial);
|
||||
(&mut *callback.lock().unwrap())(DataDeviceEvent::DnDStarted {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue