data_device: support for custom DnD icons
This commit is contained in:
parent
f3a68fb1af
commit
280decf863
|
@ -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 ]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -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(),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -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());
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -24,22 +24,32 @@
|
||||||
//! - 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
|
||||||
|
//! compositor_token.clone(), // a compositor token
|
||||||
//! None // insert a logger here
|
//! 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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue