data_device: let the compositor decide the action choice strategy
This commit is contained in:
parent
81956c8fd0
commit
35645596a8
|
@ -16,6 +16,7 @@ use smithay::wayland_server::{calloop::EventLoop, Display};
|
||||||
mod shaders;
|
mod shaders;
|
||||||
mod glium_drawer;
|
mod glium_drawer;
|
||||||
mod input_handler;
|
mod input_handler;
|
||||||
|
mod misc;
|
||||||
#[cfg(feature = "tty_launch")]
|
#[cfg(feature = "tty_launch")]
|
||||||
mod raw_drm;
|
mod raw_drm;
|
||||||
mod shell;
|
mod shell;
|
||||||
|
|
|
@ -0,0 +1,19 @@
|
||||||
|
use smithay::wayland_server::protocol::wl_data_device_manager::DndAction;
|
||||||
|
|
||||||
|
pub fn dnd_action_chooser(available: DndAction, preferred: DndAction) -> DndAction {
|
||||||
|
// if the preferred action is valid (a single action) and in the available actions, use it
|
||||||
|
// otherwise, follow a fallback stategy
|
||||||
|
if [DndAction::Move, DndAction::Copy, DndAction::Ask].contains(&preferred)
|
||||||
|
&& available.contains(preferred)
|
||||||
|
{
|
||||||
|
preferred
|
||||||
|
} else if available.contains(DndAction::Ask) {
|
||||||
|
DndAction::Ask
|
||||||
|
} else if available.contains(DndAction::Copy) {
|
||||||
|
DndAction::Copy
|
||||||
|
} else if available.contains(DndAction::Move) {
|
||||||
|
DndAction::Move
|
||||||
|
} else {
|
||||||
|
DndAction::empty()
|
||||||
|
}
|
||||||
|
}
|
|
@ -108,7 +108,7 @@ pub fn run_udev(mut display: Display, mut event_loop: EventLoop<()>, log: Logger
|
||||||
|
|
||||||
let udev_session_id = notifier.register(&mut udev_backend);
|
let udev_session_id = notifier.register(&mut udev_backend);
|
||||||
|
|
||||||
init_data_device(&mut display.borrow_mut(), 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());
|
let (mut w_seat, _) = Seat::new(&mut display.borrow_mut(), session.seat(), log.clone());
|
||||||
|
|
||||||
|
|
|
@ -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());
|
let (compositor_token, _, _, window_map) = init_shell(display, log.clone());
|
||||||
|
|
||||||
init_data_device(display, log.clone());
|
init_data_device(display, ::misc::dnd_action_chooser, log.clone());
|
||||||
|
|
||||||
let (mut seat, _) = Seat::new(display, "winit".into(), log.clone());
|
let (mut seat, _) = Seat::new(display, "winit".into(), log.clone());
|
||||||
|
|
||||||
|
|
|
@ -12,21 +12,23 @@ use wayland::seat::{AxisFrame, PointerGrab, PointerInnerHandle, Seat};
|
||||||
|
|
||||||
use super::{with_source_metadata, SeatData};
|
use super::{with_source_metadata, SeatData};
|
||||||
|
|
||||||
pub(crate) struct DnDGrab {
|
pub(crate) struct DnDGrab<F: 'static> {
|
||||||
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>>>,
|
||||||
origin: Resource<wl_surface::WlSurface>,
|
origin: Resource<wl_surface::WlSurface>,
|
||||||
seat: Seat,
|
seat: Seat,
|
||||||
|
action_choice: Arc<Mutex<F>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DnDGrab {
|
impl<F: 'static> DnDGrab<F> {
|
||||||
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 {
|
action_choice: Arc<Mutex<F>>,
|
||||||
|
) -> DnDGrab<F> {
|
||||||
DnDGrab {
|
DnDGrab {
|
||||||
data_source: source,
|
data_source: source,
|
||||||
current_focus: None,
|
current_focus: None,
|
||||||
|
@ -34,11 +36,15 @@ impl DnDGrab {
|
||||||
offer_data: None,
|
offer_data: None,
|
||||||
origin,
|
origin,
|
||||||
seat,
|
seat,
|
||||||
|
action_choice,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PointerGrab for DnDGrab {
|
impl<F> PointerGrab for DnDGrab<F>
|
||||||
|
where
|
||||||
|
F: FnMut(DndAction, DndAction) -> DndAction + Send + 'static,
|
||||||
|
{
|
||||||
fn motion(
|
fn motion(
|
||||||
&mut self,
|
&mut self,
|
||||||
_handle: &mut PointerInnerHandle,
|
_handle: &mut PointerInnerHandle,
|
||||||
|
@ -96,8 +102,14 @@ impl PointerGrab for DnDGrab {
|
||||||
// create a data offer
|
// create a data offer
|
||||||
let offer = client
|
let offer = client
|
||||||
.create_resource::<wl_data_offer::WlDataOffer>(device.version())
|
.create_resource::<wl_data_offer::WlDataOffer>(device.version())
|
||||||
.map(|offer| implement_dnd_data_offer(offer, source.clone(), offer_data.clone()))
|
.map(|offer| {
|
||||||
.unwrap();
|
implement_dnd_data_offer(
|
||||||
|
offer,
|
||||||
|
source.clone(),
|
||||||
|
offer_data.clone(),
|
||||||
|
self.action_choice.clone(),
|
||||||
|
)
|
||||||
|
}).unwrap();
|
||||||
// advertize the offer to the client
|
// advertize the offer to the client
|
||||||
device.send(wl_data_device::Event::DataOffer { id: offer.clone() });
|
device.send(wl_data_device::Event::DataOffer { id: offer.clone() });
|
||||||
with_source_metadata(source, |meta| {
|
with_source_metadata(source, |meta| {
|
||||||
|
@ -221,11 +233,15 @@ struct OfferData {
|
||||||
chosen_action: DndAction,
|
chosen_action: DndAction,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn implement_dnd_data_offer(
|
fn implement_dnd_data_offer<F>(
|
||||||
offer: NewResource<wl_data_offer::WlDataOffer>,
|
offer: NewResource<wl_data_offer::WlDataOffer>,
|
||||||
source: Resource<wl_data_source::WlDataSource>,
|
source: Resource<wl_data_source::WlDataSource>,
|
||||||
offer_data: Arc<Mutex<OfferData>>,
|
offer_data: Arc<Mutex<OfferData>>,
|
||||||
) -> Resource<wl_data_offer::WlDataOffer> {
|
action_choice: Arc<Mutex<F>>,
|
||||||
|
) -> Resource<wl_data_offer::WlDataOffer>
|
||||||
|
where
|
||||||
|
F: FnMut(DndAction, DndAction) -> DndAction + Send + 'static,
|
||||||
|
{
|
||||||
use self::wl_data_offer::Request;
|
use self::wl_data_offer::Request;
|
||||||
offer.implement(
|
offer.implement(
|
||||||
move |req, offer| {
|
move |req, offer| {
|
||||||
|
@ -296,20 +312,12 @@ fn implement_dnd_data_offer(
|
||||||
let source_actions =
|
let source_actions =
|
||||||
with_source_metadata(&source, |meta| meta.dnd_action).unwrap_or(DndAction::empty());
|
with_source_metadata(&source, |meta| meta.dnd_action).unwrap_or(DndAction::empty());
|
||||||
let possible_actions = source_actions & DndAction::from_bits_truncate(dnd_actions);
|
let possible_actions = source_actions & DndAction::from_bits_truncate(dnd_actions);
|
||||||
if possible_actions.contains(preferred_action) {
|
data.chosen_action =
|
||||||
// chose this one
|
(&mut *action_choice.lock().unwrap())(possible_actions, preferred_action);
|
||||||
data.chosen_action = preferred_action;
|
// check that the user provided callback respects that one precise action should be chosen
|
||||||
} else {
|
debug_assert!(
|
||||||
if possible_actions.contains(DndAction::Ask) {
|
[DndAction::Move, DndAction::Copy, DndAction::Ask].contains(&data.chosen_action)
|
||||||
data.chosen_action = DndAction::Ask;
|
);
|
||||||
} else if possible_actions.contains(DndAction::Copy) {
|
|
||||||
data.chosen_action = DndAction::Copy;
|
|
||||||
} else if possible_actions.contains(DndAction::Move) {
|
|
||||||
data.chosen_action = DndAction::Move;
|
|
||||||
} else {
|
|
||||||
data.chosen_action = DndAction::None;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
offer.send(wl_data_offer::Event::Action {
|
offer.send(wl_data_offer::Event::Action {
|
||||||
dnd_action: data.chosen_action.to_raw(),
|
dnd_action: data.chosen_action.to_raw(),
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,7 +1,11 @@
|
||||||
use std::sync::Mutex;
|
use std::sync::{Arc, Mutex};
|
||||||
|
|
||||||
use wayland_server::{
|
use wayland_server::{
|
||||||
protocol::{wl_data_device, wl_data_device_manager, wl_data_offer, wl_data_source},
|
protocol::{
|
||||||
|
wl_data_device,
|
||||||
|
wl_data_device_manager::{self, DndAction},
|
||||||
|
wl_data_offer, wl_data_source,
|
||||||
|
},
|
||||||
Client, Display, Global, NewResource, Resource,
|
Client, Display, Global, NewResource, Resource,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -121,17 +125,25 @@ impl SeatData {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Initialize the data device global
|
/// Initialize the data device global
|
||||||
pub fn init_data_device<L>(
|
///
|
||||||
|
/// You need to provide a `(DndAction, DndAction) -> DndAction` closure that will arbitrate
|
||||||
|
/// the choice of action resulting from a drag'n'drop session. Its first argument is the set of
|
||||||
|
/// 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, L>(
|
||||||
display: &mut Display,
|
display: &mut Display,
|
||||||
|
action_choice: F,
|
||||||
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,
|
||||||
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 global = display.create_global(3, move |new_ddm, _version| {
|
let global = display.create_global(3, move |new_ddm, _version| {
|
||||||
implement_ddm(new_ddm, log.clone());
|
implement_ddm(new_ddm, action_choice.clone(), log.clone());
|
||||||
});
|
});
|
||||||
|
|
||||||
global
|
global
|
||||||
|
@ -153,10 +165,14 @@ pub fn set_data_device_focus(seat: &Seat, client: Option<Client>) {
|
||||||
seat_data.lock().unwrap().set_focus(client);
|
seat_data.lock().unwrap().set_focus(client);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn implement_ddm(
|
fn implement_ddm<F>(
|
||||||
new_ddm: NewResource<wl_data_device_manager::WlDataDeviceManager>,
|
new_ddm: NewResource<wl_data_device_manager::WlDataDeviceManager>,
|
||||||
|
action_choice: Arc<Mutex<F>>,
|
||||||
log: ::slog::Logger,
|
log: ::slog::Logger,
|
||||||
) -> Resource<wl_data_device_manager::WlDataDeviceManager> {
|
) -> Resource<wl_data_device_manager::WlDataDeviceManager>
|
||||||
|
where
|
||||||
|
F: FnMut(DndAction, DndAction) -> DndAction + Send + 'static,
|
||||||
|
{
|
||||||
use self::wl_data_device_manager::Request;
|
use self::wl_data_device_manager::Request;
|
||||||
new_ddm.implement(
|
new_ddm.implement(
|
||||||
move |req, _ddm| match req {
|
move |req, _ddm| match req {
|
||||||
|
@ -169,7 +185,8 @@ fn implement_ddm(
|
||||||
seat.user_data()
|
seat.user_data()
|
||||||
.insert_if_missing(|| Mutex::new(SeatData::new(log.clone())));
|
.insert_if_missing(|| Mutex::new(SeatData::new(log.clone())));
|
||||||
let seat_data = seat.user_data().get::<Mutex<SeatData>>().unwrap();
|
let seat_data = seat.user_data().get::<Mutex<SeatData>>().unwrap();
|
||||||
let data_device = implement_data_device(id, seat.clone(), log.clone());
|
let data_device =
|
||||||
|
implement_data_device(id, seat.clone(), action_choice.clone(), log.clone());
|
||||||
seat_data.lock().unwrap().known_devices.push(data_device);
|
seat_data.lock().unwrap().known_devices.push(data_device);
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
|
@ -182,11 +199,15 @@ fn implement_ddm(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn implement_data_device(
|
fn implement_data_device<F>(
|
||||||
new_dd: NewResource<wl_data_device::WlDataDevice>,
|
new_dd: NewResource<wl_data_device::WlDataDevice>,
|
||||||
seat: Seat,
|
seat: Seat,
|
||||||
|
action_choice: Arc<Mutex<F>>,
|
||||||
log: ::slog::Logger,
|
log: ::slog::Logger,
|
||||||
) -> Resource<wl_data_device::WlDataDevice> {
|
) -> Resource<wl_data_device::WlDataDevice>
|
||||||
|
where
|
||||||
|
F: FnMut(DndAction, DndAction) -> DndAction + Send + 'static,
|
||||||
|
{
|
||||||
use self::wl_data_device::Request;
|
use self::wl_data_device::Request;
|
||||||
new_dd.implement(
|
new_dd.implement(
|
||||||
move |req, dd| match req {
|
move |req, dd| match req {
|
||||||
|
@ -200,7 +221,10 @@ fn implement_data_device(
|
||||||
if let Some(pointer) = seat.get_pointer() {
|
if let Some(pointer) = seat.get_pointer() {
|
||||||
if pointer.has_grab(serial) {
|
if pointer.has_grab(serial) {
|
||||||
// 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
|
||||||
pointer.set_grab(dnd_grab::DnDGrab::new(source, origin, seat.clone()), serial);
|
pointer.set_grab(
|
||||||
|
dnd_grab::DnDGrab::new(source, origin, seat.clone(), action_choice.clone()),
|
||||||
|
serial,
|
||||||
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue